Commit 273ed2bb authored by Le Dinh Trung's avatar Le Dinh Trung

Merge branch 'feature/products-and-posts' into 'dev'

Feature/products and posts

See merge request !3
parents 991637f9 ec549257
......@@ -3,6 +3,7 @@
namespace App\Http\Controllers;
use App\Models\Post;
use App\Models\PostCategory;
use App\Models\Product;
use App\Models\ProductCategory;
use Illuminate\Http\Request;
......@@ -16,7 +17,8 @@ class HomeController extends Controller
*/
public function index()
{
$productCategories = ProductCategory::noParent()->get();
$productCategories = ProductCategory::noParent()->with('children')->get();
$postCategories = PostCategory::noParent()->with('children')->get();
$latestDrops = Product::with('productAttributes')
->enabled()
->latest()
......@@ -32,16 +34,49 @@ public function index()
->latest()
->take(3)
->get();
return view('client.index', compact('latestDrops', 'productCategories', 'products', 'latestBlogs'));
return view('client.index', compact('latestDrops', 'productCategories', 'postCategories', 'products', 'latestBlogs'));
}
public function getProductsByCategory(Request $request)
public function getProductsAjax(Request $request)
{
$category = ProductCategory::findOrFail($request->cat);
$productsByCategory = Product::ofCategories($category->ids)
->enabled()
->latest()
->take(8)
->get();
return view('client.products_by_category', compact('productsByCategory'));
}
public function getProductsByCategory(Request $request, $slug)
{
$limit = $request->input('limit', 10);
$productCategories = ProductCategory::noParent()->with('children')->get();
$postCategories = PostCategory::noParent()->with('children')->get();
$category = ProductCategory::where('slug', $slug)->firstOrFail();
$productsByCategory = Product::with('productAttributes')
->enabled()
->ofCategories($category->ids)
->paginate($limit);
return view('client.product.index', compact('productCategories', 'postCategories', 'productsByCategory'));
}
public function getPostsByCategory(Request $request, $slug)
{
$limit = $request->input('limit', 10);
$productCategories = ProductCategory::noParent()->with('children')->get();
$postCategories = PostCategory::noParent()->with('children')->get();
$category = PostCategory::where('slug', $slug)->firstOrFail();
$recentPosts = Post::with('user')
->published()
->orderBy('publish_date', 'desc')
->take(5)
->get();
$postsByCategory = Post::with('user')
->published()
->ofCategories($category->ids)
->orderBy('publish_date', 'desc')
->paginate($limit);
return view('client.post.index', compact('productCategories', 'postCategories', 'recentPosts', 'postsByCategory'));
}
}
......@@ -5,15 +5,16 @@
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Cviebrock\EloquentSluggable\Sluggable;
class Post extends Model
class Post extends Model
{
const STATUS_DRAFT = 1;
const STATUS_PUBLISHED = 2;
const STATUS_FUTURE_PUBLISH = 3;
const STATUS_DELETED = 4;
use HasFactory, SoftDeletes;
use HasFactory, SoftDeletes, Sluggable;
protected $guarded = [];
......@@ -31,4 +32,18 @@ public function scopePublished($query)
{
return $query->where('status', Post::STATUS_PUBLISHED);
}
public function scopeOfCategories($query, $cat_ids)
{
return $query->whereIn('post_category_id', $cat_ids);
}
public function sluggable()
{
return [
'slug' => [
'source' => 'title'
]
];
}
}
......@@ -5,6 +5,7 @@
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Cviebrock\EloquentSluggable\Sluggable;
class PostCategory extends Model
{
......@@ -12,7 +13,7 @@ class PostCategory extends Model
const STATUS_DISABLED = 2;
const STATUS_DELETED = 3;
use HasFactory, SoftDeletes;
use HasFactory, SoftDeletes, Sluggable;
protected $guarded = [];
......@@ -31,8 +32,22 @@ public function children()
return $this->hasMany(PostCategory::class, 'parent_id');
}
public function getIdsAttribute()
{
return collect($this->children->pluck('id'))->prepend($this->id)->all();
}
public function scopeNoParent($query)
{
return $query->whereNull('parent_id');
}
public function sluggable(): array
{
return [
'slug' => [
'source' => 'name'
]
];
}
}
......@@ -5,6 +5,7 @@
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Cviebrock\EloquentSluggable\Sluggable;
class Product extends Model
{
......@@ -12,7 +13,7 @@ class Product extends Model
const STATUS_DISABLED = 2;
const STATUS_DELETED = 3;
use HasFactory, SoftDeletes;
use HasFactory, SoftDeletes, Sluggable;
protected $guarded = [];
......@@ -35,4 +36,13 @@ public function scopeOfCategories($query, $cat_ids)
{
return $query->whereIn('product_category_id', $cat_ids);
}
public function sluggable()
{
return [
'slug' => [
'source' => 'name'
]
];
}
}
......@@ -5,6 +5,7 @@
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Cviebrock\EloquentSluggable\Sluggable;
class ProductCategory extends Model
{
......@@ -12,7 +13,7 @@ class ProductCategory extends Model
const STATUS_DISABLE = 2;
const STATUS_DELETED = 3;
use HasFactory, SoftDeletes;
use HasFactory, SoftDeletes, Sluggable;
protected $guarded = [];
......@@ -40,4 +41,13 @@ public function scopeNoParent($query)
{
return $query->whereNull('parent_id');
}
public function sluggable(): array
{
return [
'slug' => [
'source' => 'name'
]
];
}
}
......@@ -2,6 +2,7 @@
namespace App\Providers;
use Illuminate\Pagination\Paginator;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
......@@ -23,6 +24,6 @@ public function register()
*/
public function boot()
{
//
Paginator::useBootstrap();
}
}
......@@ -6,6 +6,7 @@
"license": "MIT",
"require": {
"php": "^7.3|^8.0",
"cviebrock/eloquent-sluggable": "8.0",
"fruitcake/laravel-cors": "^2.0",
"guzzlehttp/guzzle": "^7.0.1",
"laravel/framework": "^8.75",
......
......@@ -161,6 +161,7 @@
Illuminate\Translation\TranslationServiceProvider::class,
Illuminate\Validation\ValidationServiceProvider::class,
Illuminate\View\ViewServiceProvider::class,
Cviebrock\EloquentSluggable\ServiceProvider::class,
/*
* Package Service Providers...
......
<?php
return [
/**
* What attributes do we use to build the slug?
* This can be a single field, like "name" which will build a slug from:
*
* $model->name;
*
* Or it can be an array of fields, like ["name", "company"], which builds a slug from:
*
* $model->name . ' ' . $model->company;
*
* If you've defined custom getters in your model, you can use those too,
* since Eloquent will call them when you request a custom attribute.
*
* Defaults to null, which uses the toString() method on your model.
*/
'source' => null,
/**
* The maximum length of a generated slug. Defaults to "null", which means
* no length restrictions are enforced. Set it to a positive integer if you
* want to make sure your slugs aren't too long.
*/
'maxLength' => null,
/**
* If you are setting a maximum length on your slugs, you may not want the
* truncated string to split a word in half. The default setting of "true"
* will ensure this, e.g. with a maxLength of 12:
*
* "my source string" -> "my-source"
*
* Setting it to "false" will simply truncate the generated slug at the
* desired length, e.g.:
*
* "my source string" -> "my-source-st"
*/
'maxLengthKeepWords' => true,
/**
* If left to "null", then use the cocur/slugify package to generate the slug
* (with the separator defined below).
*
* Set this to a closure that accepts two parameters (string and separator)
* to define a custom slugger. e.g.:
*
* 'method' => function( $string, $sep ) {
* return preg_replace('/[^a-z]+/i', $sep, $string);
* },
*
* Otherwise, this will be treated as a callable to be used. e.g.:
*
* 'method' => array('Str','slug'),
*/
'method' => null,
/**
* Separator to use when generating slugs. Defaults to a hyphen.
*/
'separator' => '-',
/**
* Enforce uniqueness of slugs? Defaults to true.
* If a generated slug already exists, an incremental numeric
* value will be appended to the end until a unique slug is found. e.g.:
*
* my-slug
* my-slug-1
* my-slug-2
*/
'unique' => true,
/**
* If you are enforcing unique slugs, the default is to add an
* incremental value to the end of the base slug. Alternatively, you
* can change this value to a closure that accepts three parameters:
* the base slug, the separator, and a Collection of the other
* "similar" slugs. The closure should return the new unique
* suffix to append to the slug.
*/
'uniqueSuffix' => null,
/**
* What is the first suffix to add to a slug to make it unique?
* For the default method of adding incremental integers, we start
* counting at 2, so the list of slugs would be, e.g.:
*
* - my-post
* - my-post-2
* - my-post-3
*/
'firstUniqueSuffix' => 2,
/**
* Should we include the trashed items when generating a unique slug?
* This only applies if the softDelete property is set for the Eloquent model.
* If set to "false", then a new slug could duplicate one that exists on a trashed model.
* If set to "true", then uniqueness is enforced across trashed and existing models.
*/
'includeTrashed' => false,
/**
* An array of slug names that can never be used for this model,
* e.g. to prevent collisions with existing routes or controller methods, etc..
* Defaults to null (i.e. no reserved names).
* Can be a static array, e.g.:
*
* 'reserved' => array('add', 'delete'),
*
* or a closure that returns an array of reserved names.
* If using a closure, it will accept one parameter: the model itself, and should
* return an array of reserved names, or null. e.g.
*
* 'reserved' => function( Model $model) {
* return $model->some_method_that_returns_an_array();
* }
*
* In the case of a slug that gets generated with one of these reserved names,
* we will do:
*
* $slug .= $separator + "1"
*
* and continue from there.
*/
'reserved' => null,
/**
* Whether to update the slug value when a model is being
* re-saved (i.e. already exists). Defaults to false, which
* means slugs are not updated.
*
* Be careful! If you are using slugs to generate URLs, then
* updating your slug automatically might change your URLs which
* is probably not a good idea from an SEO point of view.
* Only set this to true if you understand the possible consequences.
*/
'onUpdate' => false,
/**
* If the default slug engine of cocur/slugify is used, this array of
* configuration options will be used when instantiating the engine.
*/
'slugEngineOptions' => [],
];
......@@ -23,6 +23,7 @@ public function up()
$table->unsignedInteger('product_category_id');
$table->unsignedInteger('stock');
$table->unsignedTinyInteger('status')->default(Product::STATUS_ENABLED);
$table->string('slug')->nullable();
$table->softDeletes();
$table->timestamps();
});
......
......@@ -23,6 +23,7 @@ public function up()
$table->unsignedInteger('post_category_id');
$table->date('publish_date')->nullable();
$table->unsignedTinyInteger('status')->default(Post::STATUS_DRAFT);
$table->string('slug')->nullable();
$table->softDeletes();
$table->timestamps();
});
......
......@@ -21,6 +21,7 @@ public function up()
$table->unsignedInteger('parent_id')->nullable();
$table->unsignedTinyInteger('status')->default(ProductCategory::STATUS_ENABLED);
$table->unsignedInteger('ordering');
$table->string('slug')->nullable();
$table->softDeletes();
$table->timestamps();
});
......
......@@ -21,6 +21,7 @@ public function up()
$table->unsignedInteger('parent_id')->nullable();
$table->unsignedTinyInteger('status')->default(PostCategory::STATUS_ENABLED);
$table->unsignedInteger('ordering');
$table->string('slug')->nullable();
$table->softDeletes();
$table->timestamps();
});
......
......@@ -105,7 +105,7 @@
$('#productCategory{{ $productCategory->id }}').click(function() {
$.ajax({
type: "GET",
url: "{{ route('productsByCategory') }}",
url: "{{ route('productsAjax') }}",
data: {
cat: {{ $productCategory->id }},
},
......
<li style="list-style: none;" value="{{ $postCategory->id }}">
<a class="dropdown-item" href="{{ route('postByCategory', ['slug' => $postCategory->slug]) }}">
{{ $postCategory->name }}
</a>
@if (count($postCategory->children) > 0)
<ul>
@foreach ($postCategory->children as $sub)
@include('client.post.categories', ['postCategory' => $sub])
@endforeach
</ul>
@endif
</li>
@extends('layout.app')
@section('content')
<div class="container my-5">
<div class="row">
<div class="col-4">
<h2>Recent Post</h2>
@foreach ($recentPosts as $recentPost)
<div class="row my-3">
<div class="col-6"><img src="{{ $recentPost->image }}" class="card-img-top" alt="..."></div>
<div class="col-6">
<span>{{ $recentPost->publish_date }}</span>
{{-- <h6 class="card-title">{{ $recentPost->title }}</h6> --}}
<p>Posted by: {{ $recentPost->user->name }}</p>
</div>
</div>
@endforeach
</div>
<div class="col-8">
<h2>Blog</h2>
@foreach ($postsByCategory as $postByCategory)
<div class="row my-3">
<div class="col-6"><img src="{{ $postByCategory->image }}" class="card-img-top" alt="...">
</div>
<div class="col-6">
<span>{{ $postByCategory->publish_date }}</span>
<h4 class="card-title">{{ $postByCategory->title }}</h4>
<p class="mt-3">Posted by: {{ $postByCategory->user->name }}</p>
<span>{{ $postByCategory->content }}</span>
</div>
</div>
@endforeach
<div class="paginator m-3">{{ $postsByCategory->links() }}</div>
</div>
</div>
</div>
@endsection
<li style="list-style: none;" value="{{ $productCategory->id }}">
<a class="dropdown-item" href="{{ route('productByCategory', ['slug' => $productCategory->slug]) }}">
{{ $productCategory->name }}
</a>
@if (count($productCategory->children) > 0)
<ul>
@foreach ($productCategory->children as $subCategory)
@include('client.product.categories', ['productCategory' => $subCategory])
@endforeach
</ul>
@endif
</li>
@extends('layout.app')
@section('content')
<div class="container">
<div class="row mx-1 my-5">
@foreach ($productsByCategory as $productByCategory)
<div class="col-3 my-2">
<div class="card">
<img src="{{ $productByCategory->image }}" class="card-img-top" alt="...">
<div class="card-body">
<h6 class="card-title">{{ $productByCategory->name }}</h6>
<p style="max-height: 50px; overflow: auto;">{{ $productByCategory->description }}</p>
<h5>{{ $productByCategory->price }}</h5>
<div class="color">
@foreach ($productByCategory->productAttributes as $productAttribute)
<span class="dot mx-"
style="height: 20px; width: 20px;background-color:{{ $productAttribute->color }}; border-radius: 50%;
display: inline-block;"></span>
@endforeach
</div>
</div>
</div>
</div>
@endforeach
</div>
<div class="paginator m-3">{{ $productsByCategory->links() }}</div>
</div>
@endsection
<nav class="navbar navbar-expand-lg bg-light">
<div class="container-fluid">
<a class="navbar-brand" href="#">
<a class="navbar-brand" href="{{ route('home') }}">
<img src="{{ asset('assets') }}/images/logo.png" alt="" srcset="">
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent"
......@@ -10,7 +10,7 @@
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="#">Home</a>
<a class="nav-link active" aria-current="page" href="{{ route('home') }}">Home</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown"
......@@ -29,9 +29,9 @@
Product
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#">Action</a></li>
<li><a class="dropdown-item" href="#">Another action</a></li>
<li><a class="dropdown-item" href="#">Something else here</a></li>
@foreach ($productCategories as $productCategory)
@include('client.product.categories', ['productCategory' => $productCategory])
@endforeach
</ul>
</li>
<li class="nav-item dropdown">
......@@ -40,9 +40,9 @@
Blog
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#">Action</a></li>
<li><a class="dropdown-item" href="#">Another action</a></li>
<li><a class="dropdown-item" href="#">Something else here</a></li>
@foreach ($postCategories as $postCategory)
@include('client.post.categories', ['postCategory' => $postCategory])
@endforeach
</ul>
</li>
<li class="nav-item">
......
<?php
use App\Http\Controllers\HomeController;
use App\Http\Controllers\ProductController;
use Illuminate\Support\Facades\Route;
/*
......@@ -15,6 +16,8 @@
*/
Route::controller(HomeController::class)->group(function () {
Route::get('/', 'index');
Route::get('/productsByCategory', 'getProductsByCategory')->name('productsByCategory');
Route::get('/', 'index')->name('home');
Route::get('/productsAjax', 'getProductsAjax')->name('productsAjax');
Route::get('/san_pham/{slug}', 'getProductsByCategory')->name('productByCategory');
Route::get('/bai_viet/{slug}', 'getPostsByCategory')->name('postByCategory');
});
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment