Actualités Home page OK

This commit is contained in:
Jérémy Munsch 2021-09-16 19:31:57 +02:00
parent 4c340db2cc
commit 2634f28499
19 changed files with 624 additions and 4 deletions

71
app/Models/Actu.php Normal file
View file

@ -0,0 +1,71 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Cache;
class Actu extends Model
{
protected $fillable = [
'title',
'header',
'content',
'status'
];
protected static function booted()
{
static::creating(function (Actu $actu) {
$actu->header = clean($actu->header);
$actu->content = clean($actu->content);
});
static::updating(function (Actu $actu) {
$actu->header = clean($actu->header);
$actu->content = clean($actu->content);
});
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
static::created(function (Actu $actu) {
Cache::delete('ActusGlobal');
});
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
static::updated(function (Actu $actu) {
Cache::delete('ActusGlobal');
});
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
static::deleted(function (Actu $actu) {
Cache::delete('ActusGlobal');
});
}
public function getRouteKeyName()
{
return 'slug';
}
public function tags()
{
return $this->morphToMany(Tag::class, 'taggable');
}
public function notifiable()
{
return $this->morphOne(Notifications::class, 'notifiable');
}
public static function getCachedActus()
{
if (!Cache::has('ActusGlobal')) {
self::setCachedActus();
}
return Cache::get('ActusGlobal');
}
protected static function setCachedActus()
{
$actus = Actu::with('tags')->whereStatus(true)->whereFront(true)
->orderBy('updated_at', 'DESC')->limit(3)->get();
Cache::put('ActusGlobal', $actus);
}
}

37
app/Models/Tag.php Normal file
View file

@ -0,0 +1,37 @@
<?php
namespace App\Models;
use App\Actu;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
class Tag extends Model
{
public $timestamps = false;
protected $fillable = [
'name'
];
protected static function booted()
{
static::creating(function (Tag $tag) {
$tag->slug = Str::slug($tag->name);
});
static::updating(function (Tag $tag) {
$tag->slug = Str::slug($tag->name);
});
}
public function getRouteKeyName()
{
return 'slug';
}
public function actus()
{
return $this->morphedByMany(Actu::class, 'taggable');
}
}

View file

@ -2,6 +2,7 @@
namespace App\Providers;
use App\Models\Actu;
use App\Models\Articles;
use App\Models\Moments;
use Carbon\Carbon;
@ -58,6 +59,12 @@ class AppServiceProvider extends ServiceProvider
Log::critical($e->getMessage());
View::share('momentFiche', false);
}
try {
View::share('ActusGlobal', Actu::getCachedActus() ?? []);
} catch (\Exception $e) {
Log::critical($e->getMessage());
View::share('ActusGlobal', []);
}
try {
View::share('ArticlesGlobal', Articles::getCachedArticles() ?? []);
} catch (\Exception $e) {

View file

@ -83,6 +83,22 @@ return [
'inCanvas' => true,
'format' => 'png',
'trim' => true
],
'actuFront' => [
'height' => 244,
'width' => 366,
'format' => 'png',
'resize' => true,
'inCanvas' => true,
'keepRatio' => true
],
'actuFrontWebp' => [
'height' => 244,
'width' => 366,
'format' => 'webp',
'resize' => true,
'inCanvas' => true,
'keepRatio' => true
]
]
];

View file

@ -0,0 +1,33 @@
<?php
/** @var \Illuminate\Database\Eloquent\Factory $factory */
use App\Models\Actu;
use Faker\Generator as Faker;
/*
|--------------------------------------------------------------------------
| Model Factories
|--------------------------------------------------------------------------
|
| This directory should contain each of the model factory definitions for
| your application. Factories provide a convenient way to generate new
| model instances for testing / seeding your application's database.
|
*/
$i = 1;
$factory->define(Actu::class, function (Faker $faker) use (&$i) {
$title = implode(' ', $faker->unique()->words(rand(6, 12)));
$front = ($i++ <= 3 ? true : false);
return [
'title' => $title,
'slug' => \Illuminate\Support\Str::slug($title),
'header' => $faker->text(300),
'content' => $faker->text(1500),
'picture' => '/assets/images/actu_fake_image.jpg',
'picture_alt' => 'Image de champs',
'front' => $front,
'status' => $front ?: (bool)rand(0, 1)
];
});

View file

@ -0,0 +1,23 @@
<?php
/** @var \Illuminate\Database\Eloquent\Factory $factory */
use App\Models\Tag;
use Faker\Generator as Faker;
/*
|--------------------------------------------------------------------------
| Model Factories
|--------------------------------------------------------------------------
|
| This directory should contain each of the model factory definitions for
| your application. Factories provide a convenient way to generate new
| model instances for testing / seeding your application's database.
|
*/
$factory->define(Tag::class, function (Faker $faker) {
return [
'name' => implode(' ', $faker->words(rand(1, 2)))
];
});

View file

@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateTaggablesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('taggables', function (Blueprint $table) {
$table->bigInteger('tag_id');
$table->bigInteger('taggable_id');
$table->string('taggable_type', 60);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('taggables');
}
}

View file

@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateTagsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('tags', function (Blueprint $table) {
$table->id();
$table->string('slug')->unique();
$table->string('name');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('tags');
}
}

View file

@ -0,0 +1,44 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateActusTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('actus', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->string('slug');
$table->text('header');
$table->string('picture');
$table->string('picture_alt');
$table->mediumText('content');
// affiché en front page ?
$table->boolean('front');
$table->boolean('status');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('actus');
}
}

View file

@ -0,0 +1,33 @@
<?php
use App\Models\Actu;
use App\Models\Tag;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
// phpcs:ignore PSR1.Classes.ClassDeclaration.MissingNamespace
class ActuSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
DB::table('taggables')->truncate();
$this->call([
TagSeeder::class
]);
Actu::truncate();
$tags = Tag::query()->get();
factory(Actu::class, 28)->create()
->each(function ($actu) use ($tags) {
$offset = rand(0, 5);
$length = rand(1, 4);
$actu->tags()
->saveMany($tags->slice($offset, $length));
$actu->save();
});
}
}

View file

@ -0,0 +1,19 @@
<?php
use App\Models\Tag;
use Illuminate\Database\Seeder;
// phpcs:ignore PSR1.Classes.ClassDeclaration.MissingNamespace
class TagSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
Tag::truncate();
factory(Tag::class, 10)->create();
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 78 KiB

View file

@ -69,6 +69,9 @@ $engagementDarkGreen: #006633;
$engagementLightGreen: #8AC51D;
$engagementMajorBreakPoint: $big_screen + 30px;
// Actus
$actuTime: #A29094;
// Body
$body-bg: $color1;

View file

@ -717,6 +717,219 @@
}
}
& .actus {
$containerMaxWidth: 77.466rem; // 1162px
$articleMaxWith: 24.4rem; // 366px
$articleMaxHeigth: 41.6rem; // 609px
$articleGutter: 2.13rem; // 32px
display: flex;
justify-content: center;
& .container {
display: flex;
flex-direction: column;
align-items: center;
max-width: $containerMaxWidth;
width: 100%;
& .header {
margin-top: 6.53rem;
& h3 {
font-size: 3rem;
font-family: 'Veneer';
}
}
& .card {
display: flex;
flex-direction: row;
margin-top: 3.2rem;
& article {
max-height: $articleMaxHeigth;
max-width: $articleMaxWith;
background-color: #FFF;
display: flex;
flex-direction: column;
justify-content: space-between;
&:first-child {
margin-right: $articleGutter;
}
&:last-child {
margin-left: $articleGutter;
}
& > a {
position: relative;
display: block;
& i.engagement {
z-index: 1;
height: 7.999rem;
width: 8.675rem;
position: absolute;
bottom: 1.599rem;
left: 1.579rem;
content: " ";
background-image: url("~@/images/onsengage/cest-bon-detre-engage.svg");
background-size: contain;
}
& picture {
overflow: hidden;
position: relative;
width: $articleMaxWith;
& img {
object-fit: contain;
}
}
.hoverFigure {
background-color: $engagementDarkGreen;
z-index: 2;
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
opacity: 0;
display: flex;
justify-content: center;
align-items: center;
transition: opacity 500ms ease 0s;
cursor: pointer;
&:hover {
opacity: 0.88;
}
h5 {
user-select: none;
color: #ffffff;
font-size: 2.534rem;
position: relative;
font-family: 'CreamyScriptRegular';
&:before {
background-image: url('~@/images/recette/hoverRecette.png');
display: inline-block;
content: "";
width: 2.5em;
height: 1em;
position: absolute;
top: -0.8em;
left: 50%;
transform: translateX(-50%);
}
&:after {
background-image: url('~@/images/recette/hoverRecette.png');
display: inline-block;
content: "";
width: 2.5em;
height: 1em;
transform: rotate(180deg) translateX(54%);
position: absolute;
top: 1em;
left: 50%;
}
}
}
}
& header {
margin: 0 1.6rem;
margin-top: 1.6rem;
& div.tags {
& span {
font-family: "Veneer";
font-size: 1.867rem;
color: $engagementDarkGreen;
text-transform: uppercase;
}
}
& h4 {
margin-top: 1.066rem;
margin-bottom: 1.066rem;
font-family: "Catamaran-Black";
text-transform: uppercase;
color: $color5;
font-size: 1.2rem;
line-height: 1.733rem;
}
}
& section {
margin: 0 1.6rem;
font-family: "Catamaran-Regular";
color: $color5;
font-size: 1.2rem;
line-height: 1.8666rem;
}
& footer {
display: flex;
flex-direction: row;
justify-content: space-between;
margin: 0 1.6rem;
margin-top: 1.6rem;
margin-bottom: 1.066rem;
& time {
font-family: "CreamyScriptRegular";
font-size: 1.066rem;
color: $actuTime;
}
& a.link {
font-family: "Catamaran-Bold";
font-size: 0.933rem;
color: $engagementDarkGreen;
text-decoration: underline;
}
}
}
@include max-width($containerMaxWidth) {
flex-direction: column;
& article {
&:first-child {
margin-right: inherit !important;
margin-bottom: $articleGutter;
}
&:last-child {
margin-left: inherit !important;
margin-top: $articleGutter;
}
}
}
}
& .footer {
padding-top: 1.799rem;
& > a {
background-color: $engagementDarkGreen;
color: #FFF;
display: inline-block;
min-height: 2.867rem;
font-family: 'Catamaran-Bold';
font-size: 1.066rem;
font-weight: bold;
padding: 0.6rem 1rem;
border-radius: 2rem;
text-transform: uppercase;
line-height: 1.733rem;
&:hover {
background-color: #082014 !important;
text-decoration: underline !important;
cursor: pointer;
}
}
}
}
}
& #btnEnd {
max-width: 1200px;
margin: 50px auto 0 auto;

View file

@ -124,6 +124,50 @@
</div>
</section>
</section>
<section class="actus">
<div class="container">
<div class="header">
<h3>{{ __('Actualités') }}</h3>
</div>
<section class="card">
@foreach($ActusGlobal as $actu)
<article>
<a href="{{ route('brand.news.show', $actu) }}">
<div class="hoverFigure"><h5>{{ __('Lire cet article') }}</h5></div>
<picture>
<source srcset="{{ resizePublic($actu->picture, 'actuFrontWebp') }}" type="image/webp">
<source srcset="{{ resizePublic($actu->picture, 'actuFront') }}" type="image/png">
<img src="{{ resizePublic($actu->picture, 'actuFront') }}" width="366" alt="{{ $actu->picture_alt }}">
</picture>
@if($actu->tags->contains(function($tag) { return $tag->slug === Str::slug('On s\'engage'); }))
<i class="engagement"></i>
@endif
</a>
<header>
<div class="tags">
@foreach($actu->tags as $tag)
<span>{{ $tag->name }}</span>
@endforeach
</div>
<h4>{{ $actu->title }}</h4>
</header>
<section>
{{ Str::limit($actu->header, 120) }}
</section>
<footer>
<time datetime="">{{ $actu->created_at->formatLocalized('%d %B %Y') }}</time>
<a class="link" href="{{ route('brand.news.show', $actu) }}">
{{ __('Lire la suite') }}
</a>
</footer>
</article>
@endforeach
</section>
<div class="footer">
<a href="{{ route('brand.news.index') }}">{{ __('Voir toutes les actualités') }}</a>
</div>
</div>
</section>
<section class="offers">
<div class="header">
<h3>{{ __('Profitez d\'offres exclusives !') }}</h3>

View file

@ -14,6 +14,7 @@
<section>
<ul>
<li><a href="{{ route('brand.index') }}">{{__('La marque') }}</a></li>
<li><a href="{{ route('brand.news.index') }}">{{__('Actualités') }}</a></li>
<li><a href="{{ route('brand.history') }}">{{__('Notre Histoire') }}</a></li>
<li><a href="{{ route('brand.pub') }}">{{__('La Saga Publicitaire') }}</a></li>
<li><a href="{{ route('brand.our-quality-commitments') }}">{{__('Nos Engagements Qualité') }}</a></li>
@ -39,11 +40,11 @@
</a><span>{{ __('les dernières infos') }}</span>
</div>
<ul id="footer-liste-actu">
@foreach ($actusGlobal as $actualite)
@foreach ($ArticlesGlobal as $article)
<li><a href="{{ route('club.infos.child', [
'articleTag' => $actualite->type,
'publishedArticle' => $actualite
]) }}" title="{{ $actualite->title }}"><span>#{{__('Actus') }}</span> {{ $actualite->title }}</a></li>
'articleTag' => $article->type,
'publishedArticle' => $article
]) }}" title="{{ $article->title }}"><span>#{{__('Actus') }}</span> {{ $article->title }}</a></li>
@endforeach
</ul>
<ul id="footer-liste-social">

View file

@ -109,6 +109,10 @@
</picture>
</div>
<ul class="rightside">
<li>
<i class="fa fa-chevron-right" aria-hidden="true"></i>
<a href="{{ route('brand.news.index') }}">{{ __('Actualités') }}</a>
</li>
<li>
<i class="fa fa-chevron-right" aria-hidden="true"></i>
<a href="{{ route('brand.history') }}">{{ __('Notre histoire') }}</a>

View file

@ -1,5 +1,6 @@
<?php
use App\Http\Controllers\ActuController;
use App\Http\Controllers\Bo\AdsController;
use App\Http\Controllers\Bo\ArticlesController;
use App\Http\Controllers\Bo\BalancedMenusController;
@ -220,6 +221,12 @@ Route::prefix('/la-marque')
Route::view('/notre-histoire', 'front.brand.history')->name('history');
Route::view('/tracabilite', 'front.brand.traceability')->name('traceability');
Route::view('/volailles-francaises', 'front.brand.poultry')->name('french-poultry');
Route::prefix('/actualites')
->name('news.')
->group(function () {
Route::get('/{publishedActu}', [ActuController::class, 'view'])->name('show');
Route::get('/', [ActuController::class, 'index'])->name('index');
});
});
Route::prefix('/oui-cest-bon')