http://example.com/catalog/category/sub-category
.parent_id
attribute and the parent
relation. class Category extends Model { public function parent() { return $this->belongsTo(self::class); } }
slug
- a stub that reflects the section in the URL. It can be generated from the name, or specified by the user manually. Most importantly, the stub must pass the alphadash
validation alphadash
(that is, consist of letters, numbers, and signs, _
), and also be unique within the parent section. For the latter, it is enough to create a unique index in the database (parent_id, slug)
. public function getUrl() { $url = $this->slug; $category = $this; while ($category = $category->parent) { $url = $category->slug.'/'.$url; } return 'catalog/'.$url; }
$router->get('catalog/{category}', ...);
http://example.com/catalog/category
. The route will work. Now this link: http://example.com/catalog/category/sub-category
. The route will no longer work, because backslash is a parameter delimiter. Hmm, then add another parameter and make it optional: $router->get('catalog/{category}/{subcategory?}', ...);
category
identifier, then, if specified, the subcategory
subsection, etc. All this causes inconvenience and server load, the number of requests is proportional to the number of subsections. composer require kalnoy/nestedset
Schema::table('categories', function (Blueprint $table) { $table->unsignedInteger('_lft'); $table->unsignedInteger('_rgt'); });
parent
and children
relations, if they have been set, and also add a trait Kalnoy\Nestedset\NodeTrait
. After the upgrade, our model looks like this: class Category extends Model { use Kalnoy\Nestedset\NodeTrait; }
_lft
and _rgt
not filled in, so that everything _rgt
, the final touch remains: Category::fixTree();
parent_id
" the tree based on the parent_id
attribute. public function getUrl() { // $slugs = $this->ancestors()->lists('slug'); // $slugs[] = $this->slug; // return 'catalog/'.implode('/', $slugs); }
$router->get('catalog/{path}', 'CategoriesController@show') ->where('path', '[a-zA-Z0-9/_-]+');
{path}
parameter can contain not only the usual string, but also a backslash. Thus, this parameter immediately captures the entire path that follows the control word catalog
. public function show($path) { $path = explode('/', $path); }
- Category -- Sub category --- Sub sub category
- category -- category/sub-category --- category/sub-category/sub-sub-category
public function show($path) { $category = Category::where('path', '=', $path)->firstOrFail(); }
// public function generatePath() { $slugs = $this->ancestors()->lists('slug'); $slugs[] = $this->slug; $this->path = implode('/', $slugs); return $this; } // public function getUrl() { return 'catalog/'.$this->path; }
-/-
. Therefore, the path generation can be further optimized: public function generatePath() { $slug = $this->slug; $this->path = $this->isRoot() ? $slug : $this->parent->path.'/'.$slug; return $this; }
public function updateDescendantsPaths() { // $descendants = $this->descendants()->defaultOrder()->get(); // parent children $descendants->push($this)->linkNodes()->pop(); foreach ($descendants as $model) { $model->generatePath()->save(); } }
defaultOrder
here applies tree sorting. Its meaning is that in the list each section will stand after its ancestor . The path construction algorithm uses the parent, so it is necessary that the parent update its path before the path of any of its descendants is updated.parent
relationship, which is used in the path generation algorithm. If you do not use this optimization, each generatePath
call will execute a query to get the value of the parent
relationship. In this case, linkNodes
works with the collection of sections and does not make any queries to the database. Therefore, for this to work for the immediate children of the current section, you need to add it to the collection. We add the current section, we connect all sections among themselves and we remove it.slug
or parent_id
attributes have changed. If changed, we call the generatePath
method;path
attribute has changed, and if it has changed, call the updateDescendantsPaths
method. protected static function boot() { static::saving(function (self $model) { if ($model->isDirty('slug', 'parent_id')) { $model->generatePath(); } }); static::saved(function (self $model) { // , // , .. static $updating = false; if ( ! $updating && $model->isDirty('path')) { $updating = true; $model->updateDescendantsPaths(); $updating = false; } }); }
http://example.com/catalog/category/sub-catagory/product
. The main problem here is to form the correct route.(category_id, slug)
. // $router->get('catalog/{path}', function ($path) { return 'category = '.$path; })->where('path', '[a-zA-Z0-9\-/_]+'); // $router->get('catalog/{category}/{product}', function ($category, $product) { return 'category = '.$category.'<br>product = '.$product; })->where('category', '[a-zA-Z0-9\-/_]+');
category = category/sub-category/product
catalog
keyword. Need to swap routes. Then we get: category = category/sub-category product = product
http://example.com/catalog/category/sub-category
. We get the following: category = category product = sub-category
http://example.com/catalog/category/sub-category/123-product
{product}
: $router->get(...)->where('product', '[0-9]+-[a-zA-Z0-9_-]+');
$product->slug = $product->id.'-'.str_slug($product->name);
$url = 'catalog/'.$product->category->path.'/'.$product->slug;
public function show($categoryPath, $productSlug) { // $category = Category::where('path', '=', $categoryPath)->firstOrFail(); // $product = $category->products() ->where('slug', '=', $productSlug) ->firstOrFail(); }
p-
:http://example.com/catalog/category/sub-category/p-product
$router->get('catalog/{category}/p-{product}', ...);
$product->slug = str_slug($product->name);
$url = 'catalog/'.$product->category->path.'/p-'.$product->slug;
class Url extends Model { // public function model() { return $this->morphTo(); } }
$router->get('catalog/{path}', function ($path) { $url = Url::findOrFail($path); // $model = $url->model; if ($model instanceof Product) { return $this->renderProduct($model); } return $this->renderCategory($model); }) ->where('path', '[a-zA-Z0-9\-/_]+');
Url
model has a polymorphic relationship with other models and stores full paths on them. What it gives:kalnoy/nestedset
, as well as approaches to forming links to sections and products in the case where the nesting depth of sections is not limited.Source: https://habr.com/ru/post/279233/
All Articles