{"id":316961,"date":"2021-01-26T09:01:23","date_gmt":"2021-01-26T09:01:23","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=316961"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=316961","title":{"rendered":"\u041a\u0430\u043a \u043f\u043e\u0434\u0440\u0443\u0436\u0438\u0442\u044c ltree \u0438 Laravel"},"content":{"rendered":"\n<div class=\"post__text post__text_v2\" id=\"post-content-body\">\n<p>\u042f \u0443\u0447\u0430\u0441\u0442\u0432\u0443\u044e \u0432 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435 ERP \u0441\u0438\u0441\u0442\u0435\u043c\u044b, \u0432 \u0434\u0435\u0442\u0430\u043b\u0438 \u043f\u043e\u0441\u0432\u044f\u0449\u0430\u0442\u044c \u0432\u0430\u0441 \u043d\u0435 \u0431\u0443\u0434\u0443, \u044d\u0442\u043e \u0442\u0430\u0439\u043d\u0430 \u0437\u0430 \u0441\u0435\u043c\u044c\u044e \u043f\u0435\u0447\u0430\u0442\u044f\u043c\u0438, \u043d\u043e \u0441\u043a\u0430\u0436\u0443 \u043b\u0438\u0448\u044c \u043e\u0434\u043d\u043e, \u0434\u0440\u0435\u0432\u043e\u0432\u0438\u0434\u043d\u044b\u0445 \u0441\u043f\u0440\u0430\u0432\u043e\u0447\u043d\u0438\u043a\u043e\u0432 \u0443 \u043d\u0430\u0441 \u043c\u043d\u043e\u0433\u043e \u0438 \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u043e\u0441\u0442\u044c \u0438\u0445 \u043d\u0435 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0430, \u043a\u043e\u043b-\u0432\u043e \u0432 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0441\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0442\u044b\u0441\u044f\u0447, \u0430 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u043d\u0438\u043c\u0438 \u043d\u0430\u0434\u043e \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u043e \u0438 \u0443\u0434\u043e\u0431\u043d\u043e, \u043a\u0430\u043a \u0441 \u0442\u043e\u0447\u043a\u0438 \u0437\u0440\u0435\u043d\u0438\u044f \u043a\u043e\u0434\u0430, \u0442\u0430\u043a \u0441 \u0442\u043e\u0447\u043a\u0438 \u0437\u0440\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438.<\/p>\n<h2>\u0427\u0442\u043e \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e<\/h2>\n<ul>\n<li>\n<p>\u0411\u044b\u0441\u0442\u0440\u043e \u0434\u043e\u0441\u0442\u0430\u0432\u0430\u0442\u044c \u043b\u044e\u0431\u044b\u0435 \u0441\u0440\u0435\u0437\u044b \u0434\u0435\u0440\u0435\u0432\u0430 \u0438 \u0435\u0433\u043e \u0432\u0435\u0442\u0432\u0435\u0439, \u043a\u0430\u043a \u0432\u0432\u0435\u0440\u0445 \u043a \u0440\u043e\u0434\u0438\u0442\u0435\u043b\u044f\u043c, \u0442\u0430\u043a \u0438 \u0432\u043d\u0438\u0437 \u043a \u043b\u0438\u0441\u0442\u044c\u044f\u043c<\/p>\n<\/li>\n<li>\n<p>\u0423\u043c\u0435\u0442\u044c \u0442\u0430\u043a\u0436\u0435 \u0434\u043e\u0441\u0442\u0430\u0432\u0430\u0442\u044c \u0432\u0441\u0435 \u0443\u0437\u043b\u044b, \u043a\u0440\u043e\u043c\u0435 \u043b\u0438\u0441\u0442\u044c\u0435\u0432<\/p>\n<\/li>\n<li>\n<p>\u0414\u0435\u0440\u0435\u0432\u043e \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u043a\u043e\u043d\u0441\u0438\u0441\u0442\u0435\u043d\u0442\u043d\u044b\u043c, \u0442.\u0435. \u043d\u0435 \u0438\u043c\u0435\u0442\u044c \u043f\u0440\u043e\u043f\u0443\u0449\u0435\u043d\u043d\u044b\u0445 \u0443\u0437\u043b\u043e\u0432 \u0432 \u0438\u0435\u0440\u0430\u0440\u0445\u0438\u0438 \u0440\u043e\u0434\u0438\u0442\u0435\u043b\u0435\u0439<\/p>\n<\/li>\n<li>\n<p>\u041c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u043f\u0443\u0442\u0438 \u0434\u043e\u043b\u0436\u043d\u044b \u0441\u0442\u0440\u043e\u0438\u0442\u044c\u0441\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u043e\u043c<\/p>\n<\/li>\n<li>\n<p>\u041f\u0440\u0438 \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0435\u043d\u0438\u0438 \u0438\u043b\u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0438 \u0443\u0437\u043b\u0430, \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0442\u0430\u043a\u0436\u0435 \u0432\u0441\u0435 \u0434\u043e\u0447\u0435\u0440\u043d\u0438\u0435 \u0443\u0437\u043b\u044b \u0438 \u043f\u0435\u0440\u0435\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u044e\u0442\u0441\u044f \u0438\u0445 \u043f\u0443\u0442\u0438<\/p>\n<\/li>\n<li>\n<p>\u041d\u0430\u0443\u0447\u0438\u0442\u044c\u0441\u044f \u0438\u0437 \u043f\u043b\u043e\u0441\u043a\u043e\u0439 \u043a\u043e\u043b\u043b\u0435\u043a\u0446\u0438\u0438, \u0431\u044b\u0441\u0442\u0440\u043e \u043f\u043e\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0434\u0435\u0440\u0435\u0432\u043e.<\/p>\n<\/li>\n<li>\n<p>\u0422\u0430\u043a \u043a\u0430\u043a \u0441\u043f\u0440\u0430\u0432\u043e\u0447\u043d\u0438\u043a\u043e\u0432 \u043c\u043d\u043e\u0433\u043e, \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0435<\/p>\n<\/li>\n<li>\n<p>\u0422\u0430\u043a \u043a\u0430\u043a \u043f\u043b\u0430\u043d\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0432\u044b\u043d\u043e\u0441\u0438\u0442\u044c \u0432 \u0433\u0438\u0442\u0445\u0430\u0431, \u0437\u0430\u0434\u0435\u0439\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u0430\u0431\u0441\u0442\u0440\u0430\u043a\u0446\u0438\u0438 \u0438 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u044b.<\/p>\n<\/li>\n<\/ul>\n<p>\u0422\u0430\u043a \u043a\u0430\u043a \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c Postgres, \u0432\u044b\u0431\u043e\u0440 \u043f\u0430\u043b \u043d\u0430 ltree, \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u043e \u0442\u043e\u043c, \u0447\u0442\u043e \u044d\u0442\u043e \u0442\u0430\u043a\u043e\u0435 \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u0447\u0438\u0442\u0430\u0442\u044c \u0432 \u043a\u043e\u043d\u0446\u0435 \u0441\u0442\u0430\u0442\u044c\u0438.<\/p>\n<h2>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f<\/h2>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u0438\u043c\u0435\u0440 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"php\">&lt;?php  use Illuminate\\Database\\Migrations\\Migration; use Illuminate\\Support\\Facades\\App; use Illuminate\\Support\\Facades\\DB;  class CreateLtreeExtension extends Migration {     public function up(): void     {         DB::statement('CREATE EXTENSION IF NOT EXISTS LTREE');     }      public function down(): void     {         DB::statement('DROP EXTENSION IF EXISTS LTREE');     } }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<h2>\u0417\u0430\u0434\u0430\u0447\u0430<\/h2>\n<p>\u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0442\u043e\u0432\u0430\u0440\u044b \u0438 \u0435\u0441\u0442\u044c \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0438 \u0442\u043e\u0432\u0430\u0440\u043e\u0432. \u041a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0438 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0442 \u0441\u043e\u0431\u043e\u0439 \u0434\u0435\u0440\u0435\u0432\u043e \u0438 \u043c\u043e\u0433\u0443\u0442 \u0438\u043c\u0435\u0442\u044c \u043f\u043e\u0434\u0447\u0438\u043d\u0435\u043d\u043d\u044b\u0435 \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0438, \u0443\u0440\u043e\u0432\u0435\u043d\u044c \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u043e\u0441\u0442\u0438 \u043d\u0435\u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d \u0438 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043b\u044e\u0431\u043e\u0439. \u0414\u043e\u043f\u0443\u0441\u0442\u0438\u043c \u044d\u0442\u043e \u0431\u0443\u0434\u0443\u0442 \u0442\u0430\u0431\u043b\u0438\u0446\u044b.<\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0438 \u0442\u043e\u0432\u0430\u0440\u043e\u0432 (categories)<\/summary>\n<div class=\"spoiler__content\">\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<td>\n<p><strong>id<\/strong><\/p>\n<\/td>\n<td>\n<p>bigserial<\/p>\n<\/td>\n<td>\n<p>PK<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p><strong>path<\/strong><\/p>\n<\/td>\n<td>\n<p>ltree<\/p>\n<\/td>\n<td>\n<p>\u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043f\u0443\u0442\u044c<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p><strong>parent_id<\/strong><\/p>\n<\/td>\n<td>\n<p>biginteger<\/p>\n<\/td>\n<td>\n<p>\u0440\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u0441\u043a\u0430\u044f \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u044f<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<p>\u0412 path \u0431\u0443\u0434\u0443\u0442 \u0445\u0440\u0430\u043d\u0438\u0442\u044c\u0441\u044f \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u043f\u0443\u0442\u0438, \u043f\u0440\u0438\u043c\u0435\u0440<\/p>\n<p>id: 1<br \/>path: 1<br \/>parent_<em>id: null<\/p>\n<p>id: 2<br \/>path: 1.2<br \/>parent_id: 2<\/p>\n<p>\u0438 \u0442\u0434..<\/em><\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>\u0422\u043e\u0432\u0430\u0440\u044b (products)<\/summary>\n<div class=\"spoiler__content\">\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<td>\n<p><strong>id<\/strong><\/p>\n<\/td>\n<td>\n<p>bigserial<\/p>\n<\/td>\n<td>\n<p>PK<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p><strong>category_id<\/strong><\/p>\n<\/td>\n<td>\n<p>biginteger<\/p>\n<\/td>\n<td>\n<p>\u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u044f<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<\/div>\n<\/details>\n<p>\u0415\u0441\u043b\u0438 \u0432\u044b \u0443\u0436\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435 <a href=\"https:\/\/habr.com\/ru\/post\/537426\/\" rel=\"noopener noreferrer nofollow\">\u043f\u0430\u043a\u0435\u0442 \u0434\u043b\u044f Postgres<\/a>, \u0437\u043d\u0430\u0447\u0438\u0442 \u0432\u044b \u0443\u0436\u0435 \u0437\u043d\u0430\u0435\u0442\u0435, \u0447\u0442\u043e \u0432 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u044f \u043d\u043e\u0432\u044b\u0439 extension, \u0443 \u043d\u0430\u0441 \u043f\u043e\u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043d\u043e\u0432\u044b\u0439 \u0442\u0438\u043f \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043b\u044f Doctrine \u0438 \u0435\u0433\u043e \u043d\u0443\u0436\u043d\u043e \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c, \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u044d\u0442\u043e \u043d\u0435 \u0441\u043b\u043e\u0436\u043d\u043e, \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e  \u0447\u0435\u0440\u0435\u0437 composer \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u044d\u0442\u043e\u0442 \u043f\u0430\u043a\u0435\u0442 \u0438 \u0442\u043e\u0433\u0434\u0430 \u044d\u0442\u043e \u0431\u0440\u0435\u043c\u044f \u0437\u0430 \u0432\u0430\u0441 \u0441\u0434\u0435\u043b\u0430\u0435\u0442 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u043e\u043c \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u043d:<\/p>\n<pre><code class=\"bash\">composer require umbrellio\/laravel-ltree<\/code><\/pre>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u0438\u043c\u0435\u0440 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043d\u043e\u0432\u043e\u0433\u043e \u0442\u0438\u043f\u0430 \u0432 Postgres<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"php\">&lt;?php  use Illuminate\\Database\\Migrations\\Migration; use Illuminate\\Support\\Facades\\DB; use Illuminate\\Support\\Facades\\Schema; use Umbrellio\\Postgres\\Schema\\Blueprint;  class CreateCategoryExtension extends Migration {     public function up(): void     {         Schema::table('categories', function (Blueprint $table) {             $table-&gt;bigIncrements('id');             $table-&gt;bigInteger('parent_id')-&gt;nullable();             $table-&gt;ltree('path')-&gt;nullable();              $table                 -&gt;foreign('parent_id')                 -&gt;references('id')                 -&gt;on('categories');              $table-&gt;index(['parent_id']);             $table-&gt;unique(['path']);         });          DB::statement(\"COMMENT ON COLUMN categories.path IS '(DC2Type:ltree)'\");     }      public function down(): void     {         Schema::drop('categories');     } }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u041f\u0440\u0438 \u0442\u0430\u043a\u043e\u0439 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0435 \u0441\u0430\u043c\u043e\u0435 \u043f\u0440\u043e\u0441\u0442\u043e\u0435, \u0447\u0442\u043e \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442 \u043d\u0430 \u0443\u043c, \u0447\u0442\u043e\u0431\u044b \u0434\u043e\u0441\u0442\u0430\u0442\u044c \u0432\u0441\u0435 \u0434\u0435\u0440\u0435\u0432\u043e \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0439 \u0442\u043e\u0432\u0430\u0440\u043e\u0432, \u044d\u0442\u043e \u0442\u0430\u043a\u043e\u0439 \u0437\u0430\u043f\u0440\u043e\u0441:<\/p>\n<pre><code class=\"pgsql\">SELECT * FROM categories;<\/code><\/pre>\n<p>\u041d\u043e, \u044d\u0442\u043e \u043d\u0430\u043c \u0432\u0435\u0440\u043d\u0435\u0442 \u043f\u043b\u043e\u0441\u043a\u0438\u0439 \u0441\u043f\u0438\u0441\u043e\u043a \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0439, \u0430 \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u0430\u0442\u044c \u0438\u0445, \u0440\u0430\u0437\u0443\u043c\u0435\u0435\u0442\u0441\u044f, \u043d\u0430\u043c \u043d\u0430\u0434\u043e \u0432 \u0432\u0438\u0434\u0435 \u0434\u0435\u0440\u0435\u0432\u0430. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0441\u0430\u043c\u043e\u0435 \u043f\u0440\u043e\u0441\u0442\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435, \u0435\u0441\u043b\u0438 \u0431\u044b \u043c\u044b \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438 ltree, \u0431\u044b\u043b \u0431\u044b \u0442\u0430\u043a\u043e\u0439 \u0437\u0430\u043f\u0440\u043e\u0441:<\/p>\n<pre><code class=\"pgsql\">SELECT * FROM categories WHERE parent_id IS NULL<\/code><\/pre>\n<p>\u0418\u043d\u044b\u043c\u0438 \u0441\u043b\u043e\u0432\u0430\u043c\u0438, \u0442\u043e\u043b\u044c\u043a\u043e \u043a\u043e\u0440\u043d\u0435\u0432\u044b\u0435 \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0438. \u041d\u0443 \u0430 \u0434\u0430\u043b\u0435\u0435, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0440\u0435\u043a\u0443\u0440\u0441\u0438\u044e, \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u044b\u0432\u0430\u044f \u043a\u043e\u0440\u043d\u0435\u0432\u044b\u0435 \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0438, \u0431\u0443\u0434\u0435\u043c \u0434\u0435\u0440\u0433\u0430\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441 \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u043e\u0447\u0435\u0440\u043d\u0438\u0445 \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0439, \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u044f \u0442\u0443\u0434\u0430 ID \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0438:<\/p>\n<pre><code class=\"pgsql\">SELECT * FROM categories WHERE parent_id = &lt;ID&gt;<\/code><\/pre>\n<p>\u041d\u043e, \u043a\u0430\u043a \u0432\u044b \u0432\u0438\u0434\u0438\u0442\u0435, \u044d\u0442\u043e \u0434\u043e\u043b\u0433\u043e, \u043c\u0443\u0442\u043e\u0440\u043d\u043e \u0438 \u0441\u043e\u0432\u0441\u0435\u043c \u043d\u0435 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e, \u043a\u043e\u0433\u0434\u0430 \u0443 \u043d\u0430\u0441 \u0432 \u0440\u0430\u0441\u043f\u043e\u0440\u044f\u0436\u0435\u043d\u0438\u0438 \u0435\u0441\u0442\u044c ltree. \u0421 \u043d\u0438\u043c \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u0432\u0441\u0435 \u0433\u043e\u0440\u0430\u0437\u0434\u043e \u043f\u0440\u043e\u0449\u0435, \u0447\u0442\u043e\u0431\u044b \u0434\u043e\u0441\u0442\u0430\u0442\u044c \u0432\u0441\u0435 \u0434\u043e\u0447\u0435\u0440\u043d\u0438\u0435 \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0438, \u043d\u0430\u0447\u0438\u043d\u0430\u044f \u043e\u0442 \u043a\u043e\u0440\u043d\u044f, \u043d\u0443 \u0438\u043b\u0438 \u043d\u0435 \u043e\u0442 \u043a\u043e\u0440\u043d\u044f, \u0430 \u043e\u0442 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u043b\u044c\u043d\u043e\u0433\u043e \u0443\u0437\u043b\u0430, \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0442\u0430\u043a\u043e\u0433\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430:<\/p>\n<pre><code class=\"pgsql\">SELECT * FROM categories WHERE path @&gt; text2ltree('&lt;ID&gt;')<\/code><\/pre>\n<h2>\u0412\u0435\u0440\u043d\u0435\u043c\u0441\u044f \u043a Laravel<\/h2>\n<p>\u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0434\u0440\u0435\u0432\u043e\u0432\u0438\u0434\u043d\u043e\u0439 \u043c\u043e\u0434\u0435\u043b\u0438 \u0438 \u043f\u0430\u0440\u0443 \u043c\u0435\u0442\u043e\u0434\u043e\u0432, \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 ltree. \u041a\u0430\u043a \u0432\u044b \u0438\u0445 \u043f\u0438\u0441\u0430\u0442\u044c \u0447\u0435\u0440\u0435\u0437 \u0430\u0431\u0441\u0442\u0440\u0430\u043a\u0442\u043d\u044b\u0439 \u043a\u043b\u0430\u0441\u0441 \u0438\u043b\u0438 \u0447\u0435\u0440\u0435\u0437 \u0442\u0440\u0435\u0439\u0442 \u043d\u0435 \u0442\u0430\u043a \u0432\u0430\u0436\u043d\u043e, \u0434\u0435\u043b\u043e \u0432\u043a\u0443\u0441\u0430 \u043a\u0430\u0436\u0434\u043e\u0433\u043e, \u044f \u0432\u044b\u0431\u0438\u0440\u0430\u044e \u0442\u0440\u0435\u0439\u0442\u044b, \u0442.\u043a. \u0435\u0441\u043b\u0438 \u043c\u043d\u0435 \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0443\u043d\u0430\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u044c \u043c\u043e\u0434\u0435\u043b\u0438 \u043d\u0435 \u043e\u0442 Eloquent\\Model \u044f \u0432\u0441\u0435\u0433\u0434\u0430 \u0441\u043c\u043e\u0433\u0443 \u044d\u0442\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c.<\/p>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u0438\u043c\u0435\u0440 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430: LTreeInterface<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"php\">&lt;?php  namespace Umbrellio\\LTree\\Interfaces;  interface LTreeInterface {     public const AS_STRING = 1;     public const AS_ARRAY = 2;        public function getLtreeParentColumn(): string;     public function getLtreeParentId(): ?int;     public function getLtreePathColumn(): string;     public function getLtreePath($mode = self::AS_ARRAY);     public function getLtreeLevel(): int; }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u0438\u043c\u0435\u0440 \u0442\u0440\u0435\u0439\u0442\u0430: LTreeTrait<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"php\">&lt;?php  trait LTreeTrait {     abstract public function getAttribute($key);          public function getLtreeParentColumn(): string     {         return 'parent_id';     }      public function getLtreePathColumn(): string     {         return 'path';     }      public function getLtreeParentId(): ?int     {         $value = $this-&gt;getAttribute($this-&gt;getLtreeParentColumn());         return $value ? (int) $value : null;     }      public function getLtreePath($mode = LTreeInterface::AS_ARRAY)     {         $path = $this-&gt;getAttribute($this-&gt;getLtreePathColumn());         if ($mode === LTreeModelInterface::AS_ARRAY) {             return $path !== null ? explode('.', $path) : [];         }         return (string) $path;     }      public function getLtreeLevel(): int     {         return is_array($path = $this-&gt;getLtreePath()) ? count($path) : 1;     <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u0438\u043c\u0435\u0440 \u043c\u043e\u0434\u0435\u043b\u0438, \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u044e\u0449\u0435\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 LTreeInterface: Category<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"php\">&lt;?php  final class Category extends Model implements LTreeInterface {     use LTreeTrait;      protected $table = 'categories';     protected $fillable = ['parent_id', 'path'];     protected $timestamps = false; }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c, \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u043a\u043e\u0443\u043f\u043e\u0432 \u0434\u043b\u044f \u0443\u0434\u043e\u0431\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u043c\u0438 \u043f\u0443\u0442\u044f\u043c\u0438:<\/p>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u0438\u043c\u0435\u0440 \u0442\u0440\u0435\u0439\u0442\u0430: LTreeTrait<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"php\">&lt;?php  use Illuminate\\Database\\Eloquent\\Builder; use Illuminate\\Database\\Eloquent\\Model; use Illuminate\\Database\\Eloquent\\Relations\\BelongsTo; use Illuminate\\Database\\Eloquent\\Relations\\HasMany; use Illuminate\\Database\\Eloquent\\SoftDeletes; use Umbrellio\\LTree\\Collections\\LTreeCollection; use Umbrellio\\LTree\\Interfaces\\LTreeModelInterface;  trait LTreeTrait {     \/\/...    \t\tpublic function scopeParentsOf(Builder $query, array $paths): Builder     {         return $query-&gt;whereRaw(sprintf(             \"%s @&gt; array['%s']::ltree[]\",             $this-&gt;getLtreePathColumn(),             implode(\"', '\", $paths)         ));     }      public function scopeRoot(Builder $query): Builder     {         return $query-&gt;whereRaw(sprintf('nlevel(%s) = 1', $this-&gt;getLtreePathColumn()));     }      public function scopeDescendantsOf(Builder $query, LTreeModelInterface $model): Builder     {         return $query-&gt;whereRaw(sprintf(             \"({$this-&gt;getLtreePathColumn()} &lt;@ text2ltree('%s')) = true\",             $model-&gt;getLtreePath(LTreeModelInterface::AS_STRING),         ));     }      public function scopeAncestorsOf(Builder $query, LTreeModelInterface $model): Builder     {         return $query-&gt;whereRaw(sprintf(             \"({$this-&gt;getLtreePathColumn()} @&gt; text2ltree('%s')) = true\",             $model-&gt;getLtreePath(LTreeModelInterface::AS_STRING),         ));     }      public function scopeWithoutSelf(Builder $query, int $id): Builder     {         return $query-&gt;whereRaw(sprintf('%s &lt;&gt; %s', $this-&gt;getKeyName(), $id));     }<\/code><\/pre>\n<p>\u0413\u0434\u0435:<\/p>\n<ul>\n<li>\n<p><strong>scopeAncestorsOf<\/strong> &#8212; \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0434\u043e\u0441\u0442\u0430\u0432\u0430\u0442\u044c \u043d\u0430\u043c \u0432\u0441\u0435\u0445 \u0440\u043e\u0434\u0438\u0442\u0435\u043b\u0435\u0439 \u0432\u0432\u0435\u0440\u0445 \u0434\u043e \u043a\u043e\u0440\u043d\u044f (\u0432\u043a\u043b\u044e\u0447\u0430\u044f \u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u0443\u0437\u0435\u043b)<\/p>\n<\/li>\n<li>\n<p><strong>scopeDescendantsOf<\/strong> &#8212; \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0434\u043e\u0441\u0442\u0430\u0432\u0430\u0442\u044c \u0432\u0441\u0435\u0445 \u0434\u0435\u0442\u0435\u0439 \u0432\u043d\u0438\u0437 \u0434\u043e \u043b\u0438\u0441\u0442\u0438\u043a\u0430 (\u0432\u043a\u043b\u044e\u0447\u0430\u044f \u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u0443\u0437\u0435\u043b)<\/p>\n<\/li>\n<li>\n<p><strong>scopeWithoutSelf<\/strong> &#8212; \u0438\u0441\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u0443\u0437\u0435\u043b<\/p>\n<\/li>\n<li>\n<p><strong>scopeRoot<\/strong> &#8212; \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0434\u043e\u0441\u0442\u0430\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043a\u043e\u0440\u043d\u0435\u0432\u044b\u0435 \u0443\u0437\u043b\u044b 1-\u043e\u0433\u043e \u0443\u0440\u043e\u0432\u043d\u044f<\/p>\n<\/li>\n<li>\n<p><strong>scopeParentsOf<\/strong> &#8212; \u043f\u043e\u0447\u0442\u0438 \u0442\u043e\u0436\u0435 \u0441\u0430\u043c\u043e\u0435 \u0447\u0442\u043e \u0438 scopeAncestorsOf, \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u0443\u0437\u043b\u043e\u0432.<\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/details>\n<p>\u0422.\u0435. \u0434\u043e\u0431\u0430\u0432\u0438\u0432 \u0442\u0440\u0435\u0439\u0442 \u043a \u043c\u043e\u0434\u0435\u043b\u0438, \u043e\u043d\u0430 \u0443\u0436\u0435 \u0443\u043c\u0435\u0435\u0442 \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 \u043d\u0435\u0445\u0438\u0442\u0440\u044b\u0445 \u043c\u0430\u043d\u0438\u043f\u0443\u043b\u044f\u0446\u0438\u0439 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u043c\u0438 \u0432\u0435\u0442\u043a\u0430\u043c\u0438, \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u0440\u043e\u0434\u0438\u0442\u0435\u043b\u0435\u0439, \u0434\u0435\u0442\u0435\u0439 \u0438 \u0442\u0434. <\/p>\n<h2>\u0423\u0441\u043b\u043e\u0436\u043d\u0438\u043c \u0437\u0430\u0434\u0430\u0447\u0443<\/h2>\n<p>\u041f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u043c, \u0447\u0442\u043e \u0442\u043e\u0432\u0430\u0440\u044b \u043c\u043e\u0433\u0443\u0442 \u043b\u0435\u0436\u0430\u0442\u044c \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0432 \u043b\u0438\u0441\u0442\u0438\u043a\u0430\u0445, \u043d\u043e \u0438 \u0432 \u043f\u0440\u043e\u043c\u0435\u0436\u0443\u0442\u043e\u0447\u043d\u044b\u0445 \u0443\u0437\u043b\u0430\u0445. \u041d\u0430 \u0432\u0445\u043e\u0434\u0435 \u043c\u044b \u0438\u043c\u0435\u0435\u043c \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u044e &#8212; \u0443\u0440\u043e\u0432\u043d\u044f \u043b\u0438\u0441\u0442\u0438\u043a, \u043a\u0430\u043a\u043e\u0433\u043e \u0443\u0440\u043e\u0432\u043d\u044f \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u043e\u0441\u0442\u0438 \u043c\u044b \u043d\u0435 \u0437\u043d\u0430\u0435\u043c, \u0434\u0430 \u044d\u0442\u043e \u0438 \u043d\u0435 \u0432\u0430\u0436\u043d\u043e. \u041d\u0443\u0436\u043d\u043e \u0434\u043e\u0441\u0442\u0430\u0442\u044c \u0432\u0441\u0435 \u0442\u043e\u0432\u0430\u0440\u044b \u0438\u0437 \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0438, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0438\u0437 \u0432\u0441\u0435\u0445 \u0440\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0445 \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0439 \u044d\u0442\u043e\u0433\u043e \u043b\u0438\u0441\u0442\u0438\u043a\u0430. <\/p>\n<p>\u0421\u0430\u043c\u043e\u0435 \u043f\u0440\u043e\u0441\u0442\u043e\u0435, \u0447\u0442\u043e \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442 \u043d\u0430 \u0443\u043c, \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0434\u043e\u0441\u0442\u0430\u0442\u044c \u0432\u0441\u0435 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u044b \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0439, \u0430 \u043f\u043e\u0442\u043e\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u0438\u0442\u044c \u0442\u043e\u0432\u0430\u0440\u044b \u043d\u0430\u0445\u043e\u0434\u044f\u0449\u0438\u0435\u0441\u044f \u0432 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0445 \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u044f\u0445:<\/p>\n<pre><code class=\"php\">&lt;?php  \/\/ ID \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0438 (\u043b\u0438\u0441\u0442\u0438\u043a\u0430) = 15  $categories = Category::ancestorsOf(15)-&gt;get()-&gt;pluck('id')-&gt;toArray(); $products = Product::whereIn('category_id', $caregories)-&gt;get();<\/code><\/pre>\n<h2>\u0420\u0438\u0441\u0443\u0435\u043c \u0434\u0435\u0440\u0435\u0432\u043e<\/h2>\n<p>\u0414\u0430, \u043c\u044b \u043d\u0430\u0443\u0447\u0438\u043b\u0438\u0441\u044c \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u0434\u0440\u0435\u0432\u043e\u0432\u0438\u0434\u043d\u043e\u0439 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043e\u0439 \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u0411\u0414, \u043d\u043e \u043a\u0430\u043a \u0436\u0435 \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u0430\u0442\u044c \u0434\u0435\u0440\u0435\u0432\u043e, \u0432\u0441\u0435 \u0435\u0449\u0435 \u043d\u0435 \u043f\u043e\u043d\u044f\u0442\u043d\u043e.<\/p>\n<p>\u0417\u0430\u0434\u0430\u0447\u0430 \u0432\u0441\u0435 \u0442\u0430 \u0436\u0435, \u044d\u0442\u043e \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c 1 \u0437\u0430\u043f\u0440\u043e\u0441 \u0438 \u043d\u0430\u0440\u0438\u0441\u043e\u0432\u0430\u0442\u044c \u043c\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0434\u0435\u0440\u0435\u0432\u043e, \u0432\u0435\u0440\u043d\u0435\u043c\u0441\u044f \u043a \u0437\u0430\u043f\u0440\u043e\u0441\u0443:<\/p>\n<pre><code class=\"pgsql\">SELECT * FROM categories;<\/code><\/pre>\n<p>\u0414\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u044d\u0442\u043e\u0442 \u0437\u0430\u043f\u0440\u043e\u0441, \u0443 \u043d\u0430\u0441 \u0431\u044b\u043b\u043e \u0434\u0435\u0440\u0435\u0432\u043e, \u043f\u0435\u0440\u0432\u043e\u0435 \u0447\u0442\u043e \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442 \u043d\u0430 \u0443\u043c, \u044d\u0442\u043e \u043d\u0430\u043c \u043d\u0430\u0434\u043e \u043a\u0430\u043a\u0438\u043c-\u0442\u043e \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u0442\u044c \u043c\u0430\u0441\u0441\u0438\u0432 \u0432\u0438\u0434\u0430:<\/p>\n<pre><code class=\"php\">&lt;?php  $a = [   [1, '1', null],   [2, '1.2', 1],   [3, '1.2.3', 2],   [4, '4', null],   [5, '1.2.5', 2],   [6, '4.6', 4],   \/\/ ... ];<\/code><\/pre>\n<p>\u041a \u0442\u0430\u043a\u043e\u043c\u0443:<\/p>\n<pre><code class=\"php\">&lt;?php  $a = [     0 =&gt; [         'id' =&gt; 1,         'level' =&gt; 1,         'children' =&gt; [             0 =&gt; [                 'id' =&gt; 2,                 'level' =&gt; 2,                 'children' =&gt; [                     0 =&gt; [                         'id' =&gt; 3,                         'level' =&gt; 3,                         'children' =&gt; [],                     ],                     1 =&gt; [                         'id' =&gt; 5,                         'level' =&gt; 3,                         'children' =&gt; [],                     ],                 ]             ]         ]    ],    1 =&gt; [        'id' =&gt; 4,        'level' =&gt; 1,        'children' =&gt; [             0 =&gt; [                 'id' =&gt; 6,                 'level' =&gt; 2,                 'children' =&gt; [],             ]        ]    ] ];<\/code><\/pre>\n<p>\u0422\u043e\u0433\u0434\u0430 \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 \u043e\u0431\u044b\u0447\u043d\u043e\u0439 \u0440\u0435\u043a\u0443\u0440\u0441\u0438\u0438 \u043c\u044b \u0441\u043c\u043e\u0433\u043b\u0438 \u0431\u044b \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u0430\u0442\u044c \u0434\u0435\u0440\u0435\u0432\u043e, \u043f\u0440\u0438\u043c\u0435\u0440 \u0433\u0440\u0443\u0431\u044b\u0439, \u043d\u0430\u043f\u0438\u0441\u0430\u043d \u043d\u0430 \u043a\u043e\u043b\u0435\u043d\u043a\u0435, \u043d\u043e \u044d\u0442\u043e \u0441\u0435\u0439\u0447\u0430\u0441 \u043d\u0435 \u0432\u0430\u0436\u043d\u043e:<\/p>\n<pre><code class=\"php\">&lt;?php  $categories = Category::all()-&gt;toTree(); \/\/ Collection  function renderTree(Collection $collection) {    \/** @var LTreeNode $item *\/    foreach ($collection as $item) {       if ($item-&gt;children-&gt;isNotEmpty()) {          renderTree($item-&gt;children);          return;       }    }        echo str_pad($item-&gt;id, $item-&gt;level - 1, \"---\", STR_PAD_LEFT) . PHP_EOL; } <\/code><\/pre>\n<h2>\u041d\u0435\u043c\u043d\u043e\u0433\u043e \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439<\/h2>\n<p>\u0414\u043e\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c \u043d\u0430\u0448\u0443 \u043c\u043e\u0434\u0435\u043b\u044c, \u0430 \u0438\u043c\u0435\u043d\u043d\u043e \u0442\u0440\u0435\u0439\u0442, \u0434\u043e\u0431\u0430\u0432\u0438\u0432 \u0432 \u043d\u0435\u0433\u043e \u043c\u0435\u0442\u043e\u0434\u044b \u0434\u043b\u044f \u0433\u0438\u0434\u0440\u0430\u0446\u0438\u0438 \u0432 \u043e\u0441\u043e\u0431\u0443\u044e \u043a\u043e\u043b\u043b\u0435\u043a\u0446\u0438\u044e:<\/p>\n<pre><code class=\"php\">&lt;?php  trait LTreeTrait {     \/\/...      public function newCollection(array $models = []): LTreeCollection     {         return new LTreeCollection($models);     }      public function ltreeParent(): BelongsTo     {         return $this-&gt;belongsTo(static::class, $this-&gt;getLtreeParentColumn());     }      public function ltreeChildren(): HasMany     {         return $this-&gt;hasMany(static::class, $this-&gt;getLtreeParentColumn());     } }<\/code><\/pre>\n<p>\u0414\u0430\u043d\u043d\u044b\u0439 \u043c\u0435\u0442\u043e\u0434 \u0431\u0443\u0434\u0435\u0442 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u0443\u044e \u043a\u043e\u043b\u043b\u0435\u043a\u0446\u0438\u044e, \u043f\u043e\u043b\u0443\u0447\u0438\u0432 \u043d\u0430 \u0432\u0445\u043e\u0434\u0435 \u043f\u043b\u043e\u0441\u043a\u0438\u0439 \u043c\u0430\u0441\u0441\u0438\u0432, \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0435\u0442 \u0435\u0433\u043e \u043a \u0434\u0435\u0440\u0435\u0432\u0443, \u043a\u043e\u0433\u0434\u0430 \u044d\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e.<\/p>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u0438\u043c\u0435\u0440 \u043a\u043e\u043b\u043b\u0435\u043a\u0446\u0438\u0438: LTreeCollection<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"php\">&lt;?php  use Illuminate\\Database\\Eloquent\\Collection; use Illuminate\\Database\\Eloquent\\Model; use Umbrellio\\LTree\\Helpers\\LTreeBuilder; use Umbrellio\\LTree\\Helpers\\LTreeNode; use Umbrellio\\LTree\\Interfaces\\HasLTreeRelations; use Umbrellio\\LTree\\Interfaces\\LTreeInterface; use Umbrellio\\LTree\\Interfaces\\ModelInterface; use Umbrellio\\LTree\\Traits\\LTreeModelTrait;  class LTreeCollection extends Collection {     private $withLeaves = true;      public function toTree(bool $usingSort = true, bool $loadMissing = true): LTreeNode     {         if (!$model = $this-&gt;first()) {             return new LTreeNode();         }          if ($loadMissing) {             $this-&gt;loadMissingNodes($model);         }          if (!$this-&gt;withLeaves) {             $this-&gt;excludeLeaves();         }          $builder = new LTreeBuilder(             $model-&gt;getLtreePathColumn(),             $model-&gt;getKeyName(),             $model-&gt;getLtreeParentColumn()         );          return $builder-&gt;build($collection ?? $this, $usingSort);     }      public function withLeaves(bool $state = true): self     {         $this-&gt;withLeaves = $state;          return $this;     }      private function loadMissingNodes($model): self     {         if ($this-&gt;hasMissingNodes($model)) {             $this-&gt;appendAncestors($model);         }          return $this;     }      private function excludeLeaves(): void     {         foreach ($this-&gt;items as $key =&gt; $item) {             if ($item-&gt;ltreeChildren-&gt;isEmpty()) {                 $this-&gt;forget($key);             }         }     }      private function hasMissingNodes($model): bool     {         $paths = collect();          foreach ($this-&gt;items as $item) {             $paths = $paths-&gt;merge($item-&gt;getLtreePath());         }          return $paths             -&gt;unique()             -&gt;diff($this-&gt;pluck($model-&gt;getKeyName()))             -&gt;isNotEmpty();     }        private function appendAncestors($model): void     {         $paths = $this             -&gt;pluck($model-&gt;getLtreePathColumn())             -&gt;toArray();         $ids = $this             -&gt;pluck($model-&gt;getKeyName())             -&gt;toArray();          $parents = $model::parentsOf($paths)             -&gt;whereKeyNot($ids)             -&gt;get();          foreach ($parents as $item) {             $this-&gt;add($item);         }     } }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u042d\u0442\u0430 \u043a\u043e\u043b\u043b\u0435\u043a\u0446\u0438\u044f \u043f\u043e \u0441\u0443\u0442\u0438 \u043d\u0438\u0447\u0435\u043c \u043d\u0435 \u043e\u0442\u043b\u0438\u0447\u0430\u0435\u0442\u0441\u044f \u043e\u0442 \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u043e\u0439 \u0432 Laravel, \u0442.\u0435. \u0435\u0441\u043b\u0438 \u0435\u0435 \u0438\u0442\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043c\u044b \u0432\u0441\u0435 \u0435\u0449\u0435 \u0431\u0443\u0434\u0435\u043c \u0438\u043c\u0435\u0442\u044c \u043f\u043b\u043e\u0441\u043a\u0438\u0439 \u0441\u043f\u0438\u0441\u043e\u043a. \u041d\u043e \u0435\u0441\u043b\u0438 \u0432\u044b\u0437\u0432\u0430\u0442\u044c \u043c\u0435\u0442\u043e\u0434 <strong>toTree<\/strong>, \u0442\u043e \u043f\u043b\u043e\u0441\u043a\u0430\u044f \u043a\u043e\u043b\u043b\u0435\u043a\u0446\u0438\u044f, \u0433\u0434\u0435 \u0432\u0441\u0435 \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0438 \u0432\u0441\u0435\u0445 \u0443\u0440\u043e\u0432\u043d\u0435\u0439 \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u043e\u0441\u0442\u0438 \u0431\u044b\u043b\u0438 \u043d\u0430 \u043e\u0434\u043d\u043e\u043c \u0443\u0440\u043e\u0432\u043d\u0435, \u0440\u0435\u043a\u0443\u0440\u0441\u0438\u0432\u043d\u043e \u0432\u044b\u0441\u0442\u0440\u043e\u044f\u0442\u0441\u044f \u0432 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 children \u0438 \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u043c \u0438\u0437 \u043e\u0431\u044b\u0447\u043d\u043e\u0433\u043e \u043f\u043b\u043e\u0441\u043a\u043e\u0433\u043e \u043c\u0430\u0441\u0441\u0438\u0432\u0430 &#8212; \u043c\u043d\u043e\u0433\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u044b\u0439 \u043c\u0430\u0441\u0441\u0438\u0432 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 \u043d\u0430\u0448\u0435\u043c\u0443 \u0434\u0435\u0440\u0435\u0432\u0443.<\/p>\n<p>\u0410 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0435 \u0441\u043a\u043e\u0443\u043f\u044b, \u043c\u044b \u0441\u043c\u043e\u0436\u0435\u043c \u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0434\u0435\u0440\u0435\u0432\u044c\u044f \u0434\u043b\u044f \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0445 \u0432\u0435\u0442\u0432\u0435\u0439, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440:<\/p>\n<pre><code class=\"php\">&lt;?php  $categories = Category::ancestorsOf(15)-&gt;get()-&gt;toTree();<\/code><\/pre>\n<p>\u0422\u0430\u043a\u0436\u0435 \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0435\u0449\u0435 \u0434\u0432\u0430 \u043a\u043b\u0430\u0441\u0441\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0431\u0443\u0434\u0443\u0442 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0434\u0435\u0440\u0435\u0432\u043e, \u044d\u0442\u043e \u043a\u043b\u0430\u0441\u0441 &#8212; \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0449\u0438\u0439 \u0443\u0437\u0435\u043b \u0438 \u043a\u043b\u0430\u0441\u0441 \u0431\u0438\u043b\u0434\u0435\u0440\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0440\u0435\u043a\u0443\u0440\u0441\u0438\u0432\u043d\u043e \u043e\u0431\u0445\u043e\u0434\u0438\u0442\u044c \u043a\u043e\u043b\u043b\u0435\u043a\u0446\u0438\u044e \u0438 \u0432\u044b\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0442\u044c \u0443\u0437\u043b\u044b \u0432 \u043d\u0443\u0436\u043d\u043e\u043c \u043d\u0430\u043c \u043f\u043e\u0440\u044f\u0434\u043a\u0435 (\u0438 \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0435):<\/p>\n<details class=\"spoiler\">\n<summary>LTreeNode<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"php\">&lt;?php  use Illuminate\\Database\\Eloquent\\Collection; use Illuminate\\Database\\Eloquent\\Model; use InvalidArgumentException; use Umbrellio\\Common\\Contracts\\AbstractPresenter; use Umbrellio\\LTree\\Collections\\LTreeCollection; use Umbrellio\\LTree\\Interfaces\\LTreeInterface; use Umbrellio\\LTree\\Interfaces\\ModelInterface;  class LTreeNode extends AbstractPresenter {     protected $parent;     protected $children;      public function __construct($model = null)     {         parent::__construct($model);     }      public function isRoot(): bool     {         return $this-&gt;model === null;     }      public function getParent(): ?self     {         return $this-&gt;parent;     }      public function setParent(?self $parent): void     {         $this-&gt;parent = $parent;     }      public function addChild(self $node): void     {         $this             -&gt;getChildren()             -&gt;add($node);         $node-&gt;setParent($this);     }      public function getChildren(): Collection     {         if (!$this-&gt;children) {             $this-&gt;children = new Collection();         }         return $this-&gt;children;     }      public function countDescendants(): int     {         return $this             -&gt;getChildren()             -&gt;reduce(                 static function (int $count, self $node) {                     return $count + $node-&gt;countDescendants();                 },                 $this                     -&gt;getChildren()                     -&gt;count()             );     }      public function findInTree(int $id): ?self     {         if (!$this-&gt;isRoot() &amp;&amp; $this-&gt;model-&gt;getKey() === $id) {             return $this;         }         foreach ($this-&gt;getChildren() as $child) {             $result = $child-&gt;findInTree($id);             if ($result !== null) {                 return $result;             }         }         return null;     }      public function each(callable $callback): void     {         if (!$this-&gt;isRoot()) {             $callback($this);         }         $this             -&gt;getChildren()             -&gt;each(static function (self $node) use ($callback) {                 $node-&gt;each($callback);             });     }      public function toCollection(): LTreeCollection     {         $collection = new LTreeCollection();         $this-&gt;each(static function (self $item) use ($collection) {             $collection-&gt;add($item-&gt;model);         });         return $collection;     }      public function pathAsString()     {         return $this-&gt;model ? $this-&gt;model-&gt;getLtreePath(LTreeInterface::AS_STRING) : null;     }      public function toTreeArray(callable $callback)     {         return $this-&gt;fillTreeArray($this-&gt;getChildren(), $callback);     }      \/**      * Usage sortTree(['name' =&gt;'asc', 'category'=&gt;'desc'])      * or callback with arguments ($a, $b) and return -1 | 0 | 1      * @param array|callable $options      *\/     public function sortTree($options)     {         $children = $this-&gt;getChildren();         $callback = $options;         if (!is_callable($options)) {             $callback = $this-&gt;optionsToCallback($options);         }         $children-&gt;each(static function ($child) use ($callback) {             $child-&gt;sortTree($callback);         });         $this-&gt;children = $children             -&gt;sort($callback)             -&gt;values();     }      private function fillTreeArray(iterable $nodes, callable $callback)     {         $data = [];         foreach ($nodes as $node) {             $item = $callback($node);             $children = $this-&gt;fillTreeArray($node-&gt;getChildren(), $callback);             $item['children'] = $children;             $data[] = $item;         }         return $data;     }      private function optionsToCallback(array $options): callable     {         return function ($a, $b) use ($options) {             foreach ($options as $property =&gt; $sort) {                 if (!in_array(strtolower($sort), ['asc', 'desc'], true)) {                     throw new InvalidArgumentException(\"Order '${sort}'' must be asc or desc\");                 }                 $order = strtolower($sort) === 'desc' ? -1 : 1;                 $result = $a-&gt;{$property} &lt;=&gt; $b-&gt;{$property};                 if ($result !== 0) {                     return $result * $order;                 }             }             return 0;         };     } }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>LTreeBuilder<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"php\">&lt;?php  class LTreeBuilder {     private $pathField;     private $idField;     private $parentIdField;     private $nodes = [];     private $root = null;      public function __construct(string $pathField, string $idField, string $parentIdField)     {         $this-&gt;pathField = $pathField;         $this-&gt;idField = $idField;         $this-&gt;parentIdField = $parentIdField;     }      public function build(LTreeCollection $items, bool $usingSort = true): LTreeNode     {         if ($usingSort === true) {             $items = $items-&gt;sortBy($this-&gt;pathField, SORT_STRING);         }          $this-&gt;root = new LTreeNode();          foreach ($items as $item) {             $node = new LTreeNode($item);              [$id, $parentId] = $this-&gt;getNodeIds($item);              $parentNode = $this-&gt;getNode($parentId);             $parentNode-&gt;addChild($node);              $this-&gt;nodes[$id] = $node;         }         return $this-&gt;root;     }      private function getNodeIds($item): array     {         $parentId = $item-&gt;{$this-&gt;parentIdField};         $id = $item-&gt;{$this-&gt;idField};          if ($id === $parentId) {             throw new LTreeReflectionException($id);         }         return [$id, $parentId];     }      private function getNode(?int $id): LTreeNode     {         if ($id === null) {             return $this-&gt;root;         }         if (!isset($this-&gt;nodes[$id])) {             throw new LTreeUndefinedNodeException($id);         }         return $this-&gt;nodes[$id];     } }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0414\u043b\u044f \u043f\u0440\u043e\u0441\u0442\u043e\u0442\u044b \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u043a\u0438 \u0434\u0435\u0440\u0435\u0432\u0430 \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u0435\u0449\u0435 \u0434\u0432\u0430 \u0430\u0431\u0441\u0442\u0440\u0430\u043a\u0442\u043d\u044b\u0445 \u0440\u0435\u0441\u0443\u0440\u0441\u0430:<\/p>\n<details class=\"spoiler\">\n<summary>AbstractLTreeResource<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"php\">&lt;?php  namespace Umbrellio\\LTree\\Resources;  use Illuminate\\Http\\Resources\\Json\\ResourceCollection; use Illuminate\\Support\\Collection; use Umbrellio\\LTree\\Collections\\LTreeCollection;  abstract class LTreeResourceCollection extends ResourceCollection {     \/**      * @param LTreeCollection|Collection $resource      *\/     public function __construct($resource, $sort = null, bool $usingSort = true, bool $loadMissing = true)     {         $collection = $resource-&gt;toTree($usingSort, $loadMissing);          if ($sort) {             $collection-&gt;sortTree($sort);         }          parent::__construct($collection-&gt;getChildren());     } }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>AbstractLTreeResourceCollection<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"php\">&lt;?php  namespace Umbrellio\\LTree\\Resources;  use Illuminate\\Http\\Resources\\Json\\JsonResource; use Umbrellio\\LTree\\Helpers\\LTreeNode; use Umbrellio\\LTree\\Interfaces\\LTreeInterface;  \/**  * @property LTreeNode $resource  *\/ abstract class LTreeResource extends JsonResource {     final public function toArray($request)     {         return array_merge($this-&gt;toTreeArray($request, $this-&gt;resource-&gt;model), [             'children' =&gt; static::collection($this-&gt;resource-&gt;getChildren())-&gt;toArray($request),         ]);     }      \/**      * @param LTreeInterface $model      *\/     abstract protected function toTreeArray($request, $model); }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<h2>\u0412\u043f\u0435\u0440\u0435\u0434 \u043d\u0430 \u0430\u043c\u0431\u0440\u0430\u0437\u0443\u0440\u0443<\/h2>\n<p>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e \u0431\u0443\u0434\u0435\u043c \u0442\u0430\u043a, \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0434\u0432\u0430 \u0440\u0435\u0441\u0443\u0440\u0441\u0430 JsonResource:<\/p>\n<details class=\"spoiler\">\n<summary>CategoryResource<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"php\">&lt;?php  use Umbrellio\\LTree\\Helpers\\LTreeNode; use Umbrellio\\LTree\\Resources\\LTreeResource;  class CategoryResource extends LTreeResource {     public function toTreeArray($request, LTreeNode $model)     {         return [             'id' =&gt; $model-&gt;id,             'level' =&gt; $model-&gt;getLtreeLevel(),         ];     } }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>CategoryResourceCollection<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"php\">&lt;?php  use Umbrellio\\LTree\\Resources\\LTreeResourceCollection;  class CategoryResourceCollection extends LTreeResourceCollection {     public $collects = CategoryResource::class; }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u041f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u043c, \u0447\u0442\u043e \u0443 \u0432\u0430\u0441 \u0435\u0441\u0442\u044c \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 <strong>CategoryController<\/strong> \u0438 \u043c\u0435\u0442\u043e\u0434 \u0410\u041f\u0418 <strong>data<\/strong> \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u044e\u0449\u0438\u0439 \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0438 \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 json:<\/p>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u0438\u043c\u0435\u0440 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0430<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"php\">&lt;?php  use Illuminate\\Routing\\Controller; use Illuminate\\Http\\Request;  class CategoryController extends Controller {     \/\/...        public function data(Request $request)     {         return response()-&gt;json(             new CategoryResourceCollection(                 Category::all(),                 ['id' =&gt; 'asc']             )         );     }  }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0435 \u0440\u0435\u0441\u0443\u0440\u0441\u044b, \u0432\u0430\u043c \u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u0438\u043d\u0443\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u043c\u0435\u0442\u043e\u0434 <strong>toTree<\/strong>, \u0442.\u043a. \u0432\u0441\u0435 \u043c\u0435\u0442\u043e\u0434\u044b Eloquent\\Builder-\u0430 (get, all, first \u0438 \u0442\u0434) \u0432 \u043c\u043e\u0434\u0435\u043b\u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u044e\u0449\u0435\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 LtreeInterface \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u044e\u0442 LtreeCollection, \u0442\u043e \u043f\u0440\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432, \u043f\u043b\u043e\u0441\u043a\u0430\u044f \u043a\u043e\u043b\u043b\u0435\u043a\u0446\u0438\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0435\u0442\u0441\u044f \u043a \u0434\u0435\u0440\u0435\u0432\u0443, \u043f\u0440\u0438\u0447\u0435\u043c \u0431\u0435\u0437 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043a \u0411\u0414.<\/p>\n<h2>\u0427\u0438\u0442\u0430\u0442\u044c \u043d\u0430\u0443\u0447\u0438\u043b\u0438\u0441\u044c, \u043d\u0430\u0443\u0447\u0438\u043c\u0441\u044f \u043f\u0438\u0441\u0430\u0442\u044c<\/h2>\n<p>\u0412\u0441\u0435 \u043e\u043f\u0438\u0441\u0430\u043d\u043d\u043e\u0435 \u0432\u044b\u0448\u0435, \u043f\u043e\u043c\u043e\u0433\u043b\u043e \u043d\u0430\u043c \u043f\u0440\u043e\u0447\u0438\u0442\u0430\u0442\u044c \u0434\u0435\u0440\u0435\u0432\u043e \u0438\u0437 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445, \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u0443\u044e \u0432\u0435\u0442\u0432\u044c \u0438\u043b\u0438 \u0432\u0441\u0435 \u0434\u0435\u0440\u0435\u0432\u043e, \u0430 \u0442\u0430\u043a\u0436\u0435 \u043c\u044b \u043d\u0430\u0443\u0447\u0438\u043b\u0438\u0441\u044c \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u044b\u0432\u0430\u0442\u044c \u0434\u0435\u0440\u0435\u0432\u043e.<\/p>\n<p>\u041d\u043e \u0447\u0442\u043e\u0431\u044b \u044d\u0442\u043e \u0432\u0441\u0435 \u043c\u044b \u043c\u043e\u0433\u043b\u0438 \u0434\u0435\u043b\u0430\u0442\u044c, \u043c\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u044d\u0442\u043e \u0434\u0435\u0440\u0435\u0432\u043e \u0443\u043c\u0435\u0442\u044c \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0442\u044c, \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0432 \u0411\u0414 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u043f\u0443\u0442\u0438 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0443\u0437\u043b\u0430 \u043d\u0430\u0448\u0435\u0433\u043e \u0431\u0443\u0434\u0443\u0449\u0435\u0433\u043e \u0434\u0435\u0440\u0435\u0432\u0430.<\/p>\n<p>\u041f\u0435\u0440\u0432\u043e\u0435 \u0447\u0442\u043e \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442 \u043d\u0430 \u0443\u043c, \u044d\u0442\u043e \u0444\u043e\u0440\u043c\u0430 \u0441 \u0434\u0432\u0443\u043c\u044f \u043f\u043e\u043b\u044f\u043c\u0438:<\/p>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<td>\n<p><strong>id<\/strong><\/p>\n<\/td>\n<td data-colwidth=\"612\" width=\"612\">\n<p>input<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p><strong>parent_id<\/strong><\/p>\n<\/td>\n<td data-colwidth=\"612\" width=\"612\">\n<p>select<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<p>\u0422.\u0435. \u043c\u044b \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u044d\u043b\u0435\u043c\u0435\u043d\u0442, \u0438 \u0435\u0441\u043b\u0438 \u043d\u0430\u043c \u043d\u0443\u0436\u0435\u043d \u043a\u043e\u0440\u043d\u0435\u0432\u043e\u0439, \u0442\u043e <strong>parent_id<\/strong><em> <\/em>\u043d\u0435 \u0437\u0430\u043f\u043e\u043b\u043d\u044f\u0435\u043c, \u0430 \u0435\u0441\u043b\u0438 \u043d\u0443\u0436\u043d\u043e \u0442\u043e \u0437\u0430\u043f\u043e\u043b\u043d\u044f\u0435\u043c. \u0410 <strong>path<\/strong> \u043f\u043e \u0438\u0434\u0435\u0435 \u0434\u043e\u043b\u0436\u0435\u043d \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u0438\u0438 <strong>id<\/strong> \u0438 <strong>parent_id.<\/strong><\/p>\n<p>\u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0441\u0435\u0440\u0432\u0438\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u043c \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u043f\u043e\u0441\u043b\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u043d\u0430\u0448\u0435\u0439 \u043c\u043e\u0434\u0435\u043b\u0438.<\/p>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u0438\u043c\u0435\u0440 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u044e\u0449\u0435\u0433\u043e path<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"php\">&lt;?php  namespace Umbrellio\\LTree\\Services;  use Illuminate\\Database\\Eloquent\\Model; use Umbrellio\\LTree\\Helpers\\LTreeHelper; use Umbrellio\\LTree\\Interfaces\\LTreeModelInterface; use Umbrellio\\LTree\\Interfaces\\LTreeServiceInterface;  final class LTreeService implements LTreeServiceInterface {     private $helper;      public function __construct(LTreeHelper $helper)     {         $this-&gt;helper = $helper;     }      public function createPath(LTreeModelInterface $model): void     {         $this-&gt;helper-&gt;buildPath($model);     }      public function updatePath(LTreeModelInterface $model): void     {         $columns = array_intersect_key($model-&gt;getAttributes(), array_flip($model-&gt;getLtreeProxyUpdateColumns()));          $this-&gt;helper-&gt;moveNode($model, $model-&gt;ltreeParent, $columns);         $this-&gt;helper-&gt;buildPath($model);     }      public function dropDescendants(LTreeModelInterface $model): void     {         $columns = array_intersect_key($model-&gt;getAttributes(), array_flip($model-&gt;getLtreeProxyDeleteColumns()));          $this-&gt;helper-&gt;dropDescendants($model, $columns);     } }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<ul>\n<li>\n<p><strong>createPath<\/strong> &#8212; \u0441\u043e\u0437\u0434\u0430\u0435\u0442 path \u0434\u043b\u044f \u043d\u043e\u0432\u043e\u0433\u043e \u0443\u0437\u043b\u0430, \u043d\u0443\u0436\u043d\u043e \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u043f\u043e\u0441\u043b\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f<\/p>\n<\/li>\n<li>\n<p><strong>updatePath<\/strong> &#8212; \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442 path \u043f\u0440\u0438 \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438. \u0422\u0443\u0442 \u0432\u0430\u0436\u043d\u043e \u043f\u043e\u043d\u0438\u043c\u0430\u0442\u044c, \u0447\u0442\u043e \u043c\u0435\u043d\u044f\u044f <strong>path<\/strong> \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u0443\u0437\u043b\u0430, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0442\u0430\u043a\u0436\u0435 \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u0438 \u043f\u0443\u0442\u0438 \u0432\u0441\u0435\u0445 \u0435\u0433\u043e \u0434\u043e\u0447\u0435\u0440\u043d\u0438\u0445 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432, \u0438 \u0436\u0435\u043b\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u044d\u0442\u043e \u043e\u0434\u043d\u0438\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u043c, \u0442.\u043a. \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u0435\u0441\u043b\u0438 \u0434\u043e\u0447\u0435\u0440\u043d\u0438\u0445 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u0431\u0443\u0434\u0435\u0442 1000 &#8212; \u0434\u0435\u043b\u0430\u0442\u044c \u0442\u044b\u0441\u044f\u0447\u0443 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043d\u0430 UPDATE \u043a\u0430\u043a-\u0442\u043e \u043d\u0435 \u043a\u043e\u043c\u0438\u043b\u044c\u0444\u043e.<\/p>\n<p>\u0442.\u0435. \u0435\u0441\u043b\u0438 \u043c\u044b \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0430\u0435\u043c \u0443\u0437\u0435\u043b \u0432 \u0434\u0440\u0443\u0433\u0443\u044e \u043f\u043e\u0434\u0432\u0435\u0442\u0432\u044c, \u0442\u043e \u0432\u043c\u0435\u0441\u0442\u0435 \u0441 \u043d\u0438\u043c \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0430\u044e\u0442\u0441\u044f \u0442\u0430\u043a\u0436\u0435 \u0438 \u0432\u0441\u0435 \u0435\u0433\u043e \u0434\u0435\u0442\u0438.<\/p>\n<\/li>\n<li>\n<p><strong>dropDescendants<\/strong> &#8212; \u043c\u0435\u0442\u043e\u0434 \u0443\u0434\u0430\u043b\u044f\u044e\u0449\u0438\u0439 \u0432\u0441\u0435\u0445 \u0434\u0435\u0442\u0435\u0439, \u043f\u0440\u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0438 \u0443\u0437\u043b\u0430, \u0442\u0443\u0442 \u0442\u043e\u0436\u0435 \u0432\u0430\u0436\u043d\u043e \u043f\u043e\u043d\u0438\u043c\u0430\u0442\u044c \u043e\u0447\u0435\u0440\u0435\u0434\u043d\u043e\u0441\u0442\u044c, \u043d\u0435\u043b\u044c\u0437\u044f \u0443\u0434\u0430\u043b\u0438\u0442\u044c \u0443\u0437\u0435\u043b, \u043d\u0435 \u0443\u0434\u0430\u043b\u0438\u0432 \u0434\u0435\u0442\u0435\u0439, \u0432\u044b \u0441\u0434\u0435\u043b\u0430\u0435\u0442\u0435 \u0434\u0435\u0440\u0435\u0432\u043e \u043d\u0435\u043a\u043e\u043d\u0441\u0438\u0441\u0442\u0435\u043d\u0442\u043d\u044b\u043c, \u0430 \u0434\u0435\u0442\u0435\u0439 \u043d\u0443\u0436\u043d\u043e \u0443\u0434\u0430\u043b\u044f\u0442\u044c \u043f\u0440\u0435\u0436\u0434\u0435, \u0447\u0435\u043c \u0431\u0443\u0434\u0435\u0442\u0435 \u0443\u0434\u0430\u043b\u044f\u0442\u044c \u0443\u0437\u0435\u043b.<\/p>\n<\/li>\n<\/ul>\n<p>\u041c\u0435\u0442\u043e\u0434\u044b <em>getLtreeProxyDeleteColumns<\/em> \u0438 <em>getLtreeProxyUpdateColumns<\/em> &#8212; \u043d\u0443\u0436\u043d\u044b \u0434\u043b\u044f \u043f\u0440\u043e\u043a\u0441\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u043e\u043b\u0435\u0439 \u0442\u0438\u043f\u0430 <em>deleted_at, updated_at, editor_id<\/em> \u0438 \u0434\u0440\u0443\u0433\u0438\u0445 \u043f\u043e\u043b\u0435\u0439, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0432\u044b \u0442\u0430\u043a\u0436\u0435 \u0445\u043e\u0442\u0438\u0442\u0435 \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u0432 \u0434\u043e\u0447\u0435\u0440\u043d\u0438\u0445 \u0443\u0437\u043b\u0430\u0445 \u043f\u0440\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0438 \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u0443\u0437\u043b\u0430 \u0438\u043b\u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0438. <\/p>\n<pre><code class=\"php\">&lt;?php  class CategoryService {    private LTreeService $service;       public function __construct (LTreeService $service)    {       $this-&gt;service = $service;     }       public function create (array $data): void    {        $model = App::make(Category::class);        $model-&gt;fill($data);        $model-&gt;save();              \/\/ \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043f\u0443\u0442\u044c \u0434\u043b\u044f \u0443\u0437\u043b\u0430        $this-&gt;service-&gt;createPath($model);    } }<\/code><\/pre>\n<p>\u041a\u043e\u043d\u0435\u0447\u043d\u043e, \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u043f\u0438\u043b\u0438\u0442\u044c \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043c\u0430\u0433\u0438\u0438, \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u044d\u0432\u0435\u043d\u0442\u044b, \u043b\u0438\u0441\u0442\u0435\u043d\u0435\u0440\u044b \u0438 \u0434\u0435\u0440\u0433\u0430\u0442\u044c \u043c\u0435\u0442\u043e\u0434\u044b \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438, \u043d\u043e \u044f \u0431\u043e\u043b\u044c\u0448\u0435 \u043f\u0440\u0435\u0434\u043f\u043e\u0447\u0438\u0442\u0430\u044e \u0441\u0430\u043c\u043e\u043c\u0443 \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u043d\u0443\u0436\u043d\u044b\u0435 \u043c\u0435\u0442\u043e\u0434\u044b, \u0442\u0430\u043a \u0445\u043e\u0442\u044c \u0435\u0441\u0442\u044c \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043f\u043e\u043d\u0438\u043c\u0430\u043d\u0438\u044f, \u0447\u0442\u043e \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0437\u0430 \u043a\u0443\u043b\u0438\u0441\u0430\u043c\u0438 + \u043c\u043e\u0436\u043d\u043e \u0432\u0441\u0435 \u044d\u0442\u043e \u043e\u0431\u0435\u0440\u043d\u0443\u0442\u044c \u0432 \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u044e \u0438 \u043d\u0435 \u0431\u043e\u044f\u0442\u044c\u0441\u044f, \u0447\u0442\u043e \u0433\u0434\u0435-\u0442\u043e \u0432\u044b\u043b\u0435\u0437\u0435\u0442 Deadlock.<\/p>\n<h2>\u041f\u043e\u0434\u0432\u0435\u0434\u0435\u043c \u0438\u0442\u043e\u0433<\/h2>\n<p>\u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 Postgres \/ PHP \/ Laravel \u0438 \u0443 \u0432\u0430\u0441 \u0435\u0441\u0442\u044c \u043f\u043e\u0442\u0440\u0435\u0431\u043d\u043e\u0441\u0442\u044c \u0432 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 \u0434\u0440\u0435\u0432\u043e\u0432\u0438\u0434\u043d\u044b\u0445 \u0441\u043f\u0440\u0430\u0432\u043e\u0447\u043d\u0438\u043a\u043e\u0432 (\u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0438, \u0433\u0440\u0443\u043f\u043f\u044b \u0438 \u0442\u0434), \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u043e\u0441\u0442\u044c \u043d\u0435\u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u043d\u0430\u044f, \u0438 \u0432\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u0431\u044b\u0441\u0442\u0440\u043e \u0438 \u043f\u0440\u043e\u0441\u0442\u043e \u0434\u043e\u0441\u0442\u0430\u0432\u0430\u0442\u044c \u043b\u044e\u0431\u044b\u0435 \u0441\u0440\u0435\u0437\u044b \u0432\u0435\u0442\u0432\u0435\u0439, \u0432\u0430\u043c \u0442\u043e\u0447\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u043b\u0435\u0437\u0435\u043d \u044d\u0442\u043e\u0442 \u043f\u0430\u043a\u0435\u0442.<\/p>\n<p><strong>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0435\u0433\u043e \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e:<\/strong><\/p>\n<ol>\n<li>\n<p>\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c \u0432 composer: <a href=\"https:\/\/github.com\/umbrellio\/laravel-ltree\" rel=\"noopener noreferrer nofollow\">umbrellio\/laravel-ltree<\/a><\/p>\n<\/li>\n<li>\n<p>\u041f\u0438\u0448\u0435\u0442\u0435 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u044e \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u0442\u0438\u043f\u0430 <em>ltree<\/em> (\u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442\u0435 parent_id \u0438 path \u0432 \u0432\u0430\u0448\u0443 \u0442\u0430\u0431\u043b\u0438\u0446\u0443-\u0441\u043f\u0440\u0430\u0432\u043e\u0447\u043d\u0438\u043a)<\/p>\n<\/li>\n<li>\n<p>\u0418\u043c\u043f\u043b\u0435\u043c\u0435\u043d\u0442\u0438\u0440\u0443\u0435\u0442\u0435 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 <em>LTreeModelInterface<\/em> \u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0435 \u0442\u0440\u0435\u0439\u0442 <em>LTreeModelTrait \u0432 Eloquent\\Model-\u0438<\/em>.<\/p>\n<\/li>\n<li>\n<p>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435 \u0441\u0435\u0440\u0432\u0438\u0441 <em>LTreeService<\/em> \u043f\u0440\u0438 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f\u0445 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \/ \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u043c\u043e\u0434\u0435\u043b\u0438<\/p>\n<\/li>\n<li>\n<p>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435 \u0440\u0435\u0441\u0443\u0440\u0441\u044b <em>LTreeResource<\/em> \u0438 <em>LTreeResourceCollection<\/em>, \u0435\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 SPA<\/p>\n<\/li>\n<\/ol>\n<h2>\u0420\u0435\u0441\u0443\u0440\u0441\u044b \u0434\u043b\u044f \u0438\u0437\u0443\u0447\u0435\u043d\u0438\u044f<\/h2>\n<ul>\n<li>\n<p>https:\/\/postgrespro.ru\/docs\/postgresql\/13\/ltree &#8212;  \u0442\u0443\u0442 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f Postgres, \u0441 \u043f\u0440\u0438\u043c\u0435\u0440\u0430\u043c\u0438 \u0438 \u043d\u0430 \u0440\u0443\u0441\u0441\u043a\u043e\u043c \u044f\u0437\u044b\u043a\u0435<\/p>\n<\/li>\n<li>\n<p>https:\/\/www.postgresql.org\/docs\/13\/ltree.html -\u0434\u043b\u044f \u0442\u0435\u0445, \u043a\u0442\u043e \u043b\u044e\u0431\u0438\u0442 \u0447\u0438\u0442\u0430\u0442\u044c \u043c\u0430\u043d\u0443\u0430\u043b\u044b \u0432 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u0435<\/p>\n<\/li>\n<\/ul>\n<p>\u0421\u043f\u0430\u0441\u0438\u0431\u043e \u0437\u0430 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435.<\/p>\n<\/div>\n<p> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/post\/539244\/\"> https:\/\/habr.com\/ru\/post\/539244\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"\n<div class=\"post__text post__text_v2\" id=\"post-content-body\">\n<p>\u042f \u0443\u0447\u0430\u0441\u0442\u0432\u0443\u044e \u0432 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435 ERP \u0441\u0438\u0441\u0442\u0435\u043c\u044b, \u0432 \u0434\u0435\u0442\u0430\u043b\u0438 \u043f\u043e\u0441\u0432\u044f\u0449\u0430\u0442\u044c \u0432\u0430\u0441 \u043d\u0435 \u0431\u0443\u0434\u0443, \u044d\u0442\u043e \u0442\u0430\u0439\u043d\u0430 \u0437\u0430 \u0441\u0435\u043c\u044c\u044e \u043f\u0435\u0447\u0430\u0442\u044f\u043c\u0438, \u043d\u043e \u0441\u043a\u0430\u0436\u0443 \u043b\u0438\u0448\u044c \u043e\u0434\u043d\u043e, \u0434\u0440\u0435\u0432\u043e\u0432\u0438\u0434\u043d\u044b\u0445 \u0441\u043f\u0440\u0430\u0432\u043e\u0447\u043d\u0438\u043a\u043e\u0432 \u0443 \u043d\u0430\u0441 \u043c\u043d\u043e\u0433\u043e \u0438 \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u043e\u0441\u0442\u044c \u0438\u0445 \u043d\u0435 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0430, \u043a\u043e\u043b-\u0432\u043e \u0432 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0441\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0442\u044b\u0441\u044f\u0447, \u0430 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u043d\u0438\u043c\u0438 \u043d\u0430\u0434\u043e \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u043e \u0438 \u0443\u0434\u043e\u0431\u043d\u043e, \u043a\u0430\u043a \u0441 \u0442\u043e\u0447\u043a\u0438 \u0437\u0440\u0435\u043d\u0438\u044f \u043a\u043e\u0434\u0430, \u0442\u0430\u043a \u0441 \u0442\u043e\u0447\u043a\u0438 \u0437\u0440\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438.<\/p>\n<h2>\u0427\u0442\u043e \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e<\/h2>\n<ul>\n<li>\n<p>\u0411\u044b\u0441\u0442\u0440\u043e \u0434\u043e\u0441\u0442\u0430\u0432\u0430\u0442\u044c \u043b\u044e\u0431\u044b\u0435 \u0441\u0440\u0435\u0437\u044b \u0434\u0435\u0440\u0435\u0432\u0430 \u0438 \u0435\u0433\u043e \u0432\u0435\u0442\u0432\u0435\u0439, \u043a\u0430\u043a \u0432\u0432\u0435\u0440\u0445 \u043a \u0440\u043e\u0434\u0438\u0442\u0435\u043b\u044f\u043c, \u0442\u0430\u043a \u0438 \u0432\u043d\u0438\u0437 \u043a \u043b\u0438\u0441\u0442\u044c\u044f\u043c<\/p>\n<\/li>\n<li>\n<p>\u0423\u043c\u0435\u0442\u044c \u0442\u0430\u043a\u0436\u0435 \u0434\u043e\u0441\u0442\u0430\u0432\u0430\u0442\u044c \u0432\u0441\u0435 \u0443\u0437\u043b\u044b, \u043a\u0440\u043e\u043c\u0435 \u043b\u0438\u0441\u0442\u044c\u0435\u0432<\/p>\n<\/li>\n<li>\n<p>\u0414\u0435\u0440\u0435\u0432\u043e \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u043a\u043e\u043d\u0441\u0438\u0441\u0442\u0435\u043d\u0442\u043d\u044b\u043c, \u0442.\u0435. \u043d\u0435 \u0438\u043c\u0435\u0442\u044c \u043f\u0440\u043e\u043f\u0443\u0449\u0435\u043d\u043d\u044b\u0445 \u0443\u0437\u043b\u043e\u0432 \u0432 \u0438\u0435\u0440\u0430\u0440\u0445\u0438\u0438 \u0440\u043e\u0434\u0438\u0442\u0435\u043b\u0435\u0439<\/p>\n<\/li>\n<li>\n<p>\u041c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u043f\u0443\u0442\u0438 \u0434\u043e\u043b\u0436\u043d\u044b \u0441\u0442\u0440\u043e\u0438\u0442\u044c\u0441\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u043e\u043c<\/p>\n<\/li>\n<li>\n<p>\u041f\u0440\u0438 \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0435\u043d\u0438\u0438 \u0438\u043b\u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0438 \u0443\u0437\u043b\u0430, \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0442\u0430\u043a\u0436\u0435 \u0432\u0441\u0435 \u0434\u043e\u0447\u0435\u0440\u043d\u0438\u0435 \u0443\u0437\u043b\u044b \u0438 \u043f\u0435\u0440\u0435\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u044e\u0442\u0441\u044f \u0438\u0445 \u043f\u0443\u0442\u0438<\/p>\n<\/li>\n<li>\n<p>\u041d\u0430\u0443\u0447\u0438\u0442\u044c\u0441\u044f \u0438\u0437 \u043f\u043b\u043e\u0441\u043a\u043e\u0439 \u043a\u043e\u043b\u043b\u0435\u043a\u0446\u0438\u0438, \u0431\u044b\u0441\u0442\u0440\u043e \u043f\u043e\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0434\u0435\u0440\u0435\u0432\u043e.<\/p>\n<\/li>\n<li>\n<p>\u0422\u0430\u043a \u043a\u0430\u043a \u0441\u043f\u0440\u0430\u0432\u043e\u0447\u043d\u0438\u043a\u043e\u0432 \u043c\u043d\u043e\u0433\u043e, \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0435<\/p>\n<\/li>\n<li>\n<p>\u0422\u0430\u043a \u043a\u0430\u043a \u043f\u043b\u0430\u043d\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0432\u044b\u043d\u043e\u0441\u0438\u0442\u044c \u0432 \u0433\u0438\u0442\u0445\u0430\u0431, \u0437\u0430\u0434\u0435\u0439\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u0430\u0431\u0441\u0442\u0440\u0430\u043a\u0446\u0438\u0438 \u0438 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u044b.<\/p>\n<\/li>\n<\/ul>\n<p>\u0422\u0430\u043a \u043a\u0430\u043a \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c Postgres, \u0432\u044b\u0431\u043e\u0440 \u043f\u0430\u043b \u043d\u0430 ltree, \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u043e \u0442\u043e\u043c, \u0447\u0442\u043e \u044d\u0442\u043e \u0442\u0430\u043a\u043e\u0435 \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u0447\u0438\u0442\u0430\u0442\u044c \u0432 \u043a\u043e\u043d\u0446\u0435 \u0441\u0442\u0430\u0442\u044c\u0438.<\/p>\n<h2>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f<\/h2>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u0438\u043c\u0435\u0440 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"php\">&lt;?php  use Illuminate\\Database\\Migrations\\Migration; use Illuminate\\Support\\Facades\\App; use Illuminate\\Support\\Facades\\DB;  class CreateLtreeExtension extends Migration {     public function up(): void     {         DB::statement('CREATE EXTENSION IF NOT EXISTS LTREE');     }      public function down(): void     {         DB::statement('DROP EXTENSION IF EXISTS LTREE');     } }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<h2>\u0417\u0430\u0434\u0430\u0447\u0430<\/h2>\n<p>\u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0442\u043e\u0432\u0430\u0440\u044b \u0438 \u0435\u0441\u0442\u044c \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0438 \u0442\u043e\u0432\u0430\u0440\u043e\u0432. \u041a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0438 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0442 \u0441\u043e\u0431\u043e\u0439 \u0434\u0435\u0440\u0435\u0432\u043e \u0438 \u043c\u043e\u0433\u0443\u0442 \u0438\u043c\u0435\u0442\u044c \u043f\u043e\u0434\u0447\u0438\u043d\u0435\u043d\u043d\u044b\u0435 \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0438, \u0443\u0440\u043e\u0432\u0435\u043d\u044c \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u043e\u0441\u0442\u0438 \u043d\u0435\u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d \u0438 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043b\u044e\u0431\u043e\u0439. \u0414\u043e\u043f\u0443\u0441\u0442\u0438\u043c \u044d\u0442\u043e \u0431\u0443\u0434\u0443\u0442 \u0442\u0430\u0431\u043b\u0438\u0446\u044b.<\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0438 \u0442\u043e\u0432\u0430\u0440\u043e\u0432 (categories)<\/summary>\n<div class=\"spoiler__content\">\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<td>\n<p><strong>id<\/strong><\/p>\n<\/td>\n<td>\n<p>bigserial<\/p>\n<\/td>\n<td>\n<p>PK<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p><strong>path<\/strong><\/p>\n<\/td>\n<td>\n<p>ltree<\/p>\n<\/td>\n<td>\n<p>\u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043f\u0443\u0442\u044c<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p><strong>parent_id<\/strong><\/p>\n<\/td>\n<td>\n<p>biginteger<\/p>\n<\/td>\n<td>\n<p>\u0440\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u0441\u043a\u0430\u044f \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u044f<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<p>\u0412 path \u0431\u0443\u0434\u0443\u0442 \u0445\u0440\u0430\u043d\u0438\u0442\u044c\u0441\u044f \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u043f\u0443\u0442\u0438, \u043f\u0440\u0438\u043c\u0435\u0440<\/p>\n<p>id: 1<br \/>path: 1<br \/>parent_<em>id: null<\/p>\n<p>id: 2<br \/>path: 1.2<br \/>parent_id: 2<\/p>\n<p>\u0438 \u0442\u0434..<\/em><\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>\u0422\u043e\u0432\u0430\u0440\u044b (products)<\/summary>\n<div class=\"spoiler__content\">\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<td>\n<p><strong>id<\/strong><\/p>\n<\/td>\n<td>\n<p>bigserial<\/p>\n<\/td>\n<td>\n<p>PK<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p><strong>category_id<\/strong><\/p>\n<\/td>\n<td>\n<p>biginteger<\/p>\n<\/td>\n<td>\n<p>\u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u044f<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<\/div>\n<\/details>\n<p>\u0415\u0441\u043b\u0438 \u0432\u044b \u0443\u0436\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435 <a href=\"https:\/\/habr.com\/ru\/post\/537426\/\" rel=\"noopener noreferrer nofollow\">\u043f\u0430\u043a\u0435\u0442 \u0434\u043b\u044f Postgres<\/a>, \u0437\u043d\u0430\u0447\u0438\u0442 \u0432\u044b \u0443\u0436\u0435 \u0437\u043d\u0430\u0435\u0442\u0435, \u0447\u0442\u043e \u0432 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u044f \u043d\u043e\u0432\u044b\u0439 extension, \u0443 \u043d\u0430\u0441 \u043f\u043e\u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043d\u043e\u0432\u044b\u0439 \u0442\u0438\u043f \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043b\u044f Doctrine \u0438 \u0435\u0433\u043e \u043d\u0443\u0436\u043d\u043e \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c, \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u044d\u0442\u043e \u043d\u0435 \u0441\u043b\u043e\u0436\u043d\u043e, \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e  \u0447\u0435\u0440\u0435\u0437 composer \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u044d\u0442\u043e\u0442 \u043f\u0430\u043a\u0435\u0442 \u0438 \u0442\u043e\u0433\u0434\u0430 \u044d\u0442\u043e \u0431\u0440\u0435\u043c\u044f \u0437\u0430 \u0432\u0430\u0441 \u0441\u0434\u0435\u043b\u0430\u0435\u0442 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u043e\u043c \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u043d:<\/p>\n<pre><code class=\"bash\">composer require umbrellio\/laravel-ltree<\/code><\/pre>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u0438\u043c\u0435\u0440 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043d\u043e\u0432\u043e\u0433\u043e \u0442\u0438\u043f\u0430 \u0432 Postgres<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"php\">&lt;?php  use Illuminate\\Database\\Migrations\\Migration; use Illuminate\\Support\\Facades\\DB; use Illuminate\\Support\\Facades\\Schema; use Umbrellio\\Postgres\\Schema\\Blueprint;  class CreateCategoryExtension extends Migration {     public function up(): void     {         Schema::table('categories', function (Blueprint $table) {             $table-&gt;bigIncrements('id');             $table-&gt;bigInteger('parent_id')-&gt;nullable();             $table-&gt;ltree('path')-&gt;nullable();              $table                 -&gt;foreign('parent_id')                 -&gt;references('id')                 -&gt;on('categories');              $table-&gt;index(['parent_id']);             $table-&gt;unique(['path']);         });          DB::statement(\"COMMENT ON COLUMN categories.path IS '(DC2Type:ltree)'\");     }      public function down(): void     {         Schema::drop('categories');     } }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u041f\u0440\u0438 \u0442\u0430\u043a\u043e\u0439 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0435 \u0441\u0430\u043c\u043e\u0435 \u043f\u0440\u043e\u0441\u0442\u043e\u0435, \u0447\u0442\u043e \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442 \u043d\u0430 \u0443\u043c, \u0447\u0442\u043e\u0431\u044b \u0434\u043e\u0441\u0442\u0430\u0442\u044c \u0432\u0441\u0435 \u0434\u0435\u0440\u0435\u0432\u043e \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0439 \u0442\u043e\u0432\u0430\u0440\u043e\u0432, \u044d\u0442\u043e \u0442\u0430\u043a\u043e\u0439 \u0437\u0430\u043f\u0440\u043e\u0441:<\/p>\n<pre><code class=\"pgsql\">SELECT * FROM categories;<\/code><\/pre>\n<p>\u041d\u043e, \u044d\u0442\u043e \u043d\u0430\u043c \u0432\u0435\u0440\u043d\u0435\u0442 \u043f\u043b\u043e\u0441\u043a\u0438\u0439 \u0441\u043f\u0438\u0441\u043e\u043a \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0439, \u0430 \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u0430\u0442\u044c \u0438\u0445, \u0440\u0430\u0437\u0443\u043c\u0435\u0435\u0442\u0441\u044f, \u043d\u0430\u043c \u043d\u0430\u0434\u043e \u0432 \u0432\u0438\u0434\u0435 \u0434\u0435\u0440\u0435\u0432\u0430. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0441\u0430\u043c\u043e\u0435 \u043f\u0440\u043e\u0441\u0442\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435, \u0435\u0441\u043b\u0438 \u0431\u044b \u043c\u044b \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438 ltree, \u0431\u044b\u043b \u0431\u044b \u0442\u0430\u043a\u043e\u0439 \u0437\u0430\u043f\u0440\u043e\u0441:<\/p>\n<pre><code class=\"pgsql\">SELECT * FROM categories WHERE parent_id IS NULL<\/code><\/pre>\n<p>\u0418\u043d\u044b\u043c\u0438 \u0441\u043b\u043e\u0432\u0430\u043c\u0438, \u0442\u043e\u043b\u044c\u043a\u043e \u043a\u043e\u0440\u043d\u0435\u0432\u044b\u0435 \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0438. \u041d\u0443 \u0430 \u0434\u0430\u043b\u0435\u0435, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0440\u0435\u043a\u0443\u0440\u0441\u0438\u044e, \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u044b\u0432\u0430\u044f \u043a\u043e\u0440\u043d\u0435\u0432\u044b\u0435 \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0438, \u0431\u0443\u0434\u0435\u043c \u0434\u0435\u0440\u0433\u0430\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441 \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u043e\u0447\u0435\u0440\u043d\u0438\u0445 \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0439, \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u044f \u0442\u0443\u0434\u0430 ID \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0438:<\/p>\n<pre><code class=\"pgsql\">SELECT * FROM categories WHERE parent_id = &lt;ID&gt;<\/code><\/pre>\n<p>\u041d\u043e, \u043a\u0430\u043a \u0432\u044b \u0432\u0438\u0434\u0438\u0442\u0435, \u044d\u0442\u043e \u0434\u043e\u043b\u0433\u043e, \u043c\u0443\u0442\u043e\u0440\u043d\u043e \u0438 \u0441\u043e\u0432\u0441\u0435\u043c \u043d\u0435 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e, \u043a\u043e\u0433\u0434\u0430 \u0443 \u043d\u0430\u0441 \u0432 \u0440\u0430\u0441\u043f\u043e\u0440\u044f\u0436\u0435\u043d\u0438\u0438 \u0435\u0441\u0442\u044c ltree. \u0421 \u043d\u0438\u043c \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u0432\u0441\u0435 \u0433\u043e\u0440\u0430\u0437\u0434\u043e \u043f\u0440\u043e\u0449\u0435, \u0447\u0442\u043e\u0431\u044b \u0434\u043e\u0441\u0442\u0430\u0442\u044c \u0432\u0441\u0435 \u0434\u043e\u0447\u0435\u0440\u043d\u0438\u0435 \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0438, \u043d\u0430\u0447\u0438\u043d\u0430\u044f \u043e\u0442 \u043a\u043e\u0440\u043d\u044f, \u043d\u0443 \u0438\u043b\u0438 \u043d\u0435 \u043e\u0442 \u043a\u043e\u0440\u043d\u044f, \u0430 \u043e\u0442 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u043b\u044c\u043d\u043e\u0433\u043e \u0443\u0437\u043b\u0430, \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0442\u0430\u043a\u043e\u0433\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430:<\/p>\n<pre><code class=\"pgsql\">SELECT * FROM categories WHERE path @&gt; text2ltree('&lt;ID&gt;')<\/code><\/pre>\n<h2>\u0412\u0435\u0440\u043d\u0435\u043c\u0441\u044f \u043a Laravel<\/h2>\n<p>\u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0434\u0440\u0435\u0432\u043e\u0432\u0438\u0434\u043d\u043e\u0439 \u043c\u043e\u0434\u0435\u043b\u0438 \u0438 \u043f\u0430\u0440\u0443 \u043c\u0435\u0442\u043e\u0434\u043e\u0432, \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 ltree. \u041a\u0430\u043a \u0432\u044b \u0438\u0445 \u043f\u0438\u0441\u0430\u0442\u044c \u0447\u0435\u0440\u0435\u0437 \u0430\u0431\u0441\u0442\u0440\u0430\u043a\u0442\u043d\u044b\u0439 \u043a\u043b\u0430\u0441\u0441 \u0438\u043b\u0438 \u0447\u0435\u0440\u0435\u0437 \u0442\u0440\u0435\u0439\u0442 \u043d\u0435 \u0442\u0430\u043a \u0432\u0430\u0436\u043d\u043e, \u0434\u0435\u043b\u043e \u0432\u043a\u0443\u0441\u0430 \u043a\u0430\u0436\u0434\u043e\u0433\u043e, \u044f \u0432\u044b\u0431\u0438\u0440\u0430\u044e \u0442\u0440\u0435\u0439\u0442\u044b, \u0442.\u043a. \u0435\u0441\u043b\u0438 \u043c\u043d\u0435 \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0443\u043d\u0430\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u044c \u043c\u043e\u0434\u0435\u043b\u0438 \u043d\u0435 \u043e\u0442 Eloquent\\Model \u044f \u0432\u0441\u0435\u0433\u0434\u0430 \u0441\u043c\u043e\u0433\u0443 \u044d\u0442\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c.<\/p>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u0438\u043c\u0435\u0440 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430: LTreeInterface<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"php\">&lt;?php  namespace Umbrellio\\LTree\\Interfaces;  interface LTreeInterface {     public const AS_STRING = 1;     public const AS_ARRAY = 2;        public function getLtreeParentColumn(): string;     public function getLtreeParentId(): ?int;     public function getLtreePathColumn(): string;     public function getLtreePath($mode = self::AS_ARRAY);     public function getLtreeLevel(): int; }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u0438\u043c\u0435\u0440 \u0442\u0440\u0435\u0439\u0442\u0430: LTreeTrait<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"php\">&lt;?php  trait LTreeTrait {     abstract public function getAttribute($key);          public function getLtreeParentColumn(): string     {         return 'parent_id';     }      public function getLtreePathColumn(): string     {         return 'path';     }      public function getLtreeParentId(): ?int     {         $value = $this-&gt;getAttribute($this-&gt;getLtreeParentColumn());         return $value ? (int) $value : null;     }      public function getLtreePath($mode = LTreeInterface::AS_ARRAY)     {         $path = $this-&gt;getAttribute($this-&gt;getLtreePathColumn());         if ($mode === LTreeModelInterface::AS_ARRAY) {             return $path !== null ? explode('.', $path) : [];         }         return (string) $path;     }      public function getLtreeLevel(): int     {         return is_array($path = $this-&gt;getLtreePath()) ? count($path) : 1;     <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u0438\u043c\u0435\u0440 \u043c\u043e\u0434\u0435\u043b\u0438, \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u044e\u0449\u0435\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 LTreeInterface: Category<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"php\">&lt;?php  final class Category extends Model implements LTreeInterface {     use LTreeTrait;      protected $table = 'categories';     protected $fillable = ['parent_id', 'path'];     protected $timestamps = false; }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c, \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u043a\u043e\u0443\u043f\u043e\u0432 \u0434\u043b\u044f \u0443\u0434\u043e\u0431\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u043c\u0438 \u043f\u0443\u0442\u044f\u043c\u0438:<\/p>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u0438\u043c\u0435\u0440 \u0442\u0440\u0435\u0439\u0442\u0430: LTreeTrait<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"php\">&lt;?php  use Illuminate\\Database\\Eloquent\\Builder; use Illuminate\\Database\\Eloquent\\Model; use Illuminate\\Database\\Eloquent\\Relations\\BelongsTo; use Illuminate\\Database\\Eloquent\\Relations\\HasMany; use Illuminate\\Database\\Eloquent\\SoftDeletes; use Umbrellio\\LTree\\Collections\\LTreeCollection; use Umbrellio\\LTree\\Interfaces\\LTreeModelInterface;  trait LTreeTrait {     \/\/...    \t\tpublic function scopeParentsOf(Builder $query, array $paths): Builder     {         return $query-&gt;whereRaw(sprintf(             \"%s @&gt; array['%s']::ltree[]\",             $this-&gt;getLtreePathColumn(),             implode(\"', '\", $paths)         ));     }      public function scopeRoot(Builder $query): Builder     {         return $query-&gt;whereRaw(sprintf('nlevel(%s) = 1', $this-&gt;getLtreePathColumn()));     }      public function scopeDescendantsOf(Builder $query, LTreeModelInterface $model): Builder     {         return $query-&gt;whereRaw(sprintf(             \"({$this-&gt;getLtreePathColumn()} &lt;@ text2ltree('%s')) = true\",             $model-&gt;getLtreePath(LTreeModelInterface::AS_STRING),         ));     }      public function scopeAncestorsOf(Builder $query, LTreeModelInterface $model): Builder     {         return $query-&gt;whereRaw(sprintf(             \"({$this-&gt;getLtreePathColumn()} @&gt; text2ltree('%s')) = true\",             $model-&gt;getLtreePath(LTreeModelInterface::AS_STRING),         ));     }      public function scopeWithoutSelf(Builder $query, int $id): Builder     {         return $query-&gt;whereRaw(sprintf('%s &lt;&gt; %s', $this-&gt;getKeyName(), $id));     }<\/code><\/pre>\n<p>\u0413\u0434\u0435:<\/p>\n<ul>\n<li>\n<p><strong>scopeAncestorsOf<\/strong> &#8212; \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0434\u043e\u0441\u0442\u0430\u0432\u0430\u0442\u044c \u043d\u0430\u043c \u0432\u0441\u0435\u0445 \u0440\u043e\u0434\u0438\u0442\u0435\u043b\u0435\u0439 \u0432\u0432\u0435\u0440\u0445 \u0434\u043e \u043a\u043e\u0440\u043d\u044f (\u0432\u043a\u043b\u044e\u0447\u0430\u044f \u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u0443\u0437\u0435\u043b)<\/p>\n<\/li>\n<li>\n<p><strong>scopeDescendantsOf<\/strong> &#8212; \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0434\u043e\u0441\u0442\u0430\u0432\u0430\u0442\u044c \u0432\u0441\u0435\u0445 \u0434\u0435\u0442\u0435\u0439 \u0432\u043d\u0438\u0437 \u0434\u043e \u043b\u0438\u0441\u0442\u0438\u043a\u0430 (\u0432\u043a\u043b\u044e\u0447\u0430\u044f \u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u0443\u0437\u0435\u043b)<\/p>\n<\/li>\n<li>\n<p><strong>scopeWithoutSelf<\/strong> &#8212; \u0438\u0441\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u0443\u0437\u0435\u043b<\/p>\n<\/li>\n<li>\n<p><strong>scopeRoot<\/strong> &#8212; \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0434\u043e\u0441\u0442\u0430\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043a\u043e\u0440\u043d\u0435\u0432\u044b\u0435 \u0443\u0437\u043b\u044b 1-\u043e\u0433\u043e \u0443\u0440\u043e\u0432\u043d\u044f<\/p>\n<\/li>\n<li>\n<p><strong>scopeParentsOf<\/strong> &#8212; \u043f\u043e\u0447\u0442\u0438 \u0442\u043e\u0436\u0435 \u0441\u0430\u043c\u043e\u0435 \u0447\u0442\u043e \u0438 scopeAncestorsOf, \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u0443\u0437\u043b\u043e\u0432.<\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/details>\n<p>\u0422.\u0435. \u0434\u043e\u0431\u0430\u0432\u0438\u0432 \u0442\u0440\u0435\u0439\u0442 \u043a \u043c\u043e\u0434\u0435\u043b\u0438, \u043e\u043d\u0430 \u0443\u0436\u0435 \u0443\u043c\u0435\u0435\u0442 \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 \u043d\u0435\u0445\u0438\u0442\u0440\u044b\u0445 \u043c\u0430\u043d\u0438\u043f\u0443\u043b\u044f\u0446\u0438\u0439 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u043c\u0438 \u0432\u0435\u0442\u043a\u0430\u043c\u0438, \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u0440\u043e\u0434\u0438\u0442\u0435\u043b\u0435\u0439, \u0434\u0435\u0442\u0435\u0439 \u0438 \u0442\u0434. <\/p>\n<h2>\u0423\u0441\u043b\u043e\u0436\u043d\u0438\u043c \u0437\u0430\u0434\u0430\u0447\u0443<\/h2>\n<p>\u041f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u043c, \u0447\u0442\u043e \u0442\u043e\u0432\u0430\u0440\u044b \u043c\u043e\u0433\u0443\u0442 \u043b\u0435\u0436\u0430\u0442\u044c \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0432 \u043b\u0438\u0441\u0442\u0438\u043a\u0430\u0445, \u043d\u043e \u0438 \u0432 \u043f\u0440\u043e\u043c\u0435\u0436\u0443\u0442\u043e\u0447\u043d\u044b\u0445 \u0443\u0437\u043b\u0430\u0445. \u041d\u0430 \u0432\u0445\u043e\u0434\u0435 \u043c\u044b \u0438\u043c\u0435\u0435\u043c \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u044e &#8212; \u0443\u0440\u043e\u0432\u043d\u044f \u043b\u0438\u0441\u0442\u0438\u043a, \u043a\u0430\u043a\u043e\u0433\u043e \u0443\u0440\u043e\u0432\u043d\u044f \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u043e\u0441\u0442\u0438 \u043c\u044b \u043d\u0435 \u0437\u043d\u0430\u0435\u043c, \u0434\u0430 \u044d\u0442\u043e \u0438 \u043d\u0435 \u0432\u0430\u0436\u043d\u043e. \u041d\u0443\u0436\u043d\u043e \u0434\u043e\u0441\u0442\u0430\u0442\u044c \u0432\u0441\u0435 \u0442\u043e\u0432\u0430\u0440\u044b \u0438\u0437 \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0438, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0438\u0437 \u0432\u0441\u0435\u0445 \u0440\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0445 \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0439 \u044d\u0442\u043e\u0433\u043e \u043b\u0438\u0441\u0442\u0438\u043a\u0430. <\/p>\n<p>\u0421\u0430\u043c\u043e\u0435 \u043f\u0440\u043e\u0441\u0442\u043e\u0435, \u0447\u0442\u043e \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442 \u043d\u0430 \u0443\u043c, \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0434\u043e\u0441\u0442\u0430\u0442\u044c \u0432\u0441\u0435 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u044b \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0439, \u0430 \u043f\u043e\u0442\u043e\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u0438\u0442\u044c \u0442\u043e\u0432\u0430\u0440\u044b \u043d\u0430\u0445\u043e\u0434\u044f\u0449\u0438\u0435\u0441\u044f \u0432 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0445 \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u044f\u0445:<\/p>\n<pre><code class=\"php\">&lt;?php  \/\/ ID \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0438 (\u043b\u0438\u0441\u0442\u0438\u043a\u0430) = 15  $categories = Category::ancestorsOf(15)-&gt;get()-&gt;pluck('id')-&gt;toArray(); $products = Product::whereIn('category_id', $caregories)-&gt;get();<\/code><\/pre>\n<h2>\u0420\u0438\u0441\u0443\u0435\u043c \u0434\u0435\u0440\u0435\u0432\u043e<\/h2>\n<p>\u0414\u0430, \u043c\u044b \u043d\u0430\u0443\u0447\u0438\u043b\u0438\u0441\u044c \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u0434\u0440\u0435\u0432\u043e\u0432\u0438\u0434\u043d\u043e\u0439 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043e\u0439 \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u0411\u0414, \u043d\u043e \u043a\u0430\u043a \u0436\u0435 \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u0430\u0442\u044c \u0434\u0435\u0440\u0435\u0432\u043e, \u0432\u0441\u0435 \u0435\u0449\u0435 \u043d\u0435 \u043f\u043e\u043d\u044f\u0442\u043d\u043e.<\/p>\n<p>\u0417\u0430\u0434\u0430\u0447\u0430 \u0432\u0441\u0435 \u0442\u0430 \u0436\u0435, \u044d\u0442\u043e \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c 1 \u0437\u0430\u043f\u0440\u043e\u0441 \u0438 \u043d\u0430\u0440\u0438\u0441\u043e\u0432\u0430\u0442\u044c \u043c\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0434\u0435\u0440\u0435\u0432\u043e, \u0432\u0435\u0440\u043d\u0435\u043c\u0441\u044f \u043a \u0437\u0430\u043f\u0440\u043e\u0441\u0443:<\/p>\n<pre><code class=\"pgsql\">SELECT * FROM categories;<\/code><\/pre>\n<p>\u0414\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u044d\u0442\u043e\u0442 \u0437\u0430\u043f\u0440\u043e\u0441, \u0443 \u043d\u0430\u0441 \u0431\u044b\u043b\u043e \u0434\u0435\u0440\u0435\u0432\u043e, \u043f\u0435\u0440\u0432\u043e\u0435 \u0447\u0442\u043e \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442 \u043d\u0430 \u0443\u043c, \u044d\u0442\u043e \u043d\u0430\u043c \u043d\u0430\u0434\u043e \u043a\u0430\u043a\u0438\u043c-\u0442\u043e \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u0442\u044c \u043c\u0430\u0441\u0441\u0438\u0432 \u0432\u0438\u0434\u0430:<\/p>\n<pre><code class=\"php\">&lt;?php  $a = [   [1, '1', null],   [2, '1.2', 1],   [3, '1.2.3', 2],   [4, '4', null],   [5, '1.2.5', 2],   [6, '4.6', 4],   \/\/ ... ];<\/code><\/pre>\n<p>\u041a \u0442\u0430\u043a\u043e\u043c\u0443:<\/p>\n<pre><code class=\"php\">&lt;?php  $a = [     0 =&gt; [         'id' =&gt; 1,         'level' =&gt; 1,         'children' =&gt; [             0 =&gt; [                 'id' =&gt; 2,                 'level' =&gt; 2,                 'children' =&gt; [                     0 =&gt; [                         'id' =&gt; 3,                         'level' =&gt; 3,                         'children' =&gt; [],                     ],                     1 =&gt; [                         'id' =&gt; 5,                         'level' =&gt; 3,                         'children' =&gt; [],                     ],                 ]             ]         ]    ],    1 =&gt; [<\/code><\/pre>\n<p><\/br><\/br><\/em><\/br><\/br><\/br><\/br><\/p>\n<\/div>\n<\/details>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-316961","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/316961","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=316961"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/316961\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=316961"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=316961"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=316961"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}