Moted's avatar

Laravel 5.x Pagination and recursive child categories.

Hey everyone, So I managed to finish my infinite nested categories. It was kind of simple and difficult at same time. I’m not really good with recursions. The reason of this post, if there is some way to optimize this code a little bit more.

So my controller looks like this:

public function index()
{
    $categories = Category::all();
    $categoriesPaged = $this->buildArray($categories->toArray());
    $categoriesPaged = $this->paginate($categoriesPaged, 15, 1);

    return view('admin.categories.index', ['pagedCategories' => $categoriesPaged, 'categories' => $categories]);
}

After getting all the categories, I transform the object toArray. The reason is pretty simple to reorder them in this genre. -Parent(Depth 0) --Children (Depth 1) ---Children (Depth 2) --Children (Depth 1) --Children (Depth 1) -Parent(Depth 0) So buildArray function looks like this:

function buildArray(array $objects, array &$result=array(), $parent=0, $depth=0)
{
    foreach ($objects as $key => $object) {
        if ($object['parent_id'] == $parent) {
            $object['depth'] = $depth;
            array_push($result, $object);
            unset($objects[$key]);
            CategoriesController::buildArray($objects, $result, $object['id'], $depth + 1);
        }
    }
    return $result;
}

The result is next and you can see all the camps they have:

array:6 [▼
  0 => array:9 [▼
    "id" => 1
    "parent_id" => 0
    "user_id" => 1
    "slug" => "categoria-1"
    "name" => "Categoria 1"
    "description" => "Descrição da categoria 1"
    "created_at" => "2016-01-10 01:15:43"
    "updated_at" => "2016-01-10 01:15:43"
    "depth" => 0
  ]
  1 => array:9 [▼
    "id" => 2
    "parent_id" => 1
    "user_id" => 1
    "slug" => "igors-sigalovs-11"
    "name" => "Igors Sigalovs"
    "description" => "Descrição da categoria 1"
    "created_at" => "2016-01-10 10:35:18"
    "updated_at" => "2016-01-10 10:35:18"
    "depth" => 1
  ]
  2 => array:9 [▼
    "id" => 9
    "parent_id" => 2
    "user_id" => 1
    "slug" => "igors-sigalovs-1"
    "name" => "Igors Sigalovs"
    "description" => "Descrição da categoria 1"
    "created_at" => "2016-01-10 12:58:06"
    "updated_at" => "2016-01-10 12:58:06"
    "depth" => 2
  ]
  3 => array:9 [▼
    "id" => 8
    "parent_id" => 1
    "user_id" => 1
    "slug" => "igors-sigalovs"
    "name" => "Igors Sigalovs"
    "description" => "Descrição da categoria 1"
    "created_at" => "2016-01-10 01:32:35"
    "updated_at" => "2016-01-10 01:32:35"
    "depth" => 1
  ]
  4 => array:9 [▼
    "id" => 6
    "parent_id" => 0
    "user_id" => 1
    "slug" => "test"
    "name" => "Test"
    "description" => "test"
    "created_at" => "2016-01-10 01:31:43"
    "updated_at" => "2016-01-10 01:31:43"
    "depth" => 0
  ]
  5 => array:9 [▶]
]

After rearranging them, I create custom paginator with laravel help:

public function paginate($items,$perPage,$pageStart=1)
{
    $page = Input::get('page', $pageStart);
    $offset = ($page * $perPage) - $perPage;
    return new LengthAwarePaginator(array_slice($items, $offset, $perPage, true), count($items), $perPage, $page, ['path' => Request::url(), 'query' => Request::query()]);;
}

And here is where my doubt about optimal solution is. Imagine that I set my $perPage variable to 2. The query would just bring 2 elements and on page 2 in order to get Parent name instead of ID, I have to pass all the categories in order to it can search though the array. So passing full data and partial data twice, seems a bit fishy. Example: https://i.gyazo.com/7215db2893c4e5034d422aa945d2edba.png In order to search though the array in view I used this solution.

@foreach($pagedCategories as $category)
<tr>
   <td>{{$category['name']}}</td>
   <td>{{$category['description']}}</td>
   <td>{{$category['slug']}}</td>
   <td>@if($category['depth'] == 0) --- @else {{$categories[array_search($category['parent_id'], array_column($categories->toArray(), 'id'))]['name']}} @endif</td>
…

It works like a charm, still it feels like there is a better solution. Also I don’t want to use nor ajax, nor jquery to get a solution. So any tips on improving this?

0 likes
4 replies
Moted's avatar

Its not exactly what I want @jlrdw . I already have what I want: https://gyazo.com/61b52fb94eae524d3184e08cac8f4437 . This example illustrates a little bit better. Though I wanted to check if someone got more optimized solution for this problem.

The code I posted is 100% functional, the problem is I'am passing 2 variables each time, to be able to get names of the parent categories in pages that parent is on other page.

Snapey's avatar

Don't you have a relationship from the category to its parent so that you can just $category->parent->name

1 like
Moted's avatar

@Snapey - I do have the relationship.

class Category extends Model
{
    protected $table = 'categories';

    protected $fillable = ['name', 'slug', 'description', 'parent_id'];


    public function users()
    {
        return $this->belongsTo('App\User');
    }

    public function subcategory()
    {
        return $this->belongsTo('App\Category', 'parent_id');
    }

    public function parent()
    {
        return $this->hasMany('App\Category', 'parent_id');
    }

}

Going to try to check the speed between one and another. Then going to report back. Thanks for idea.

Please or to participate in this conversation.