etrepat / baum

Baum is an implementation of the Nested Set pattern for Laravel's Eloquent ORM.
http://etrepat.com/baum
MIT License
2.24k stars 459 forks source link

Method getNestedList() not return result when set property $scoped #145

Closed JackieDo closed 9 years ago

JackieDo commented 9 years ago

I don't still understand that property $scoped is used for what purpose?

I have one table called Cate, such as:

id name parent_id lft rgt depth component
1 Tin tuc NULL 1 12 0 article
2 Chinh tri 1 2 7 1 article
3 Xa hoi 1 8 9 1 article
4 Kinh te 1 10 11 1 article
5 Trong 2 3 4 2 article
6 Ngoai 2 5 6 2 article
7 Bai viet NULL 13 18 0 article
8 Tai chinh 7 14 15 1 article
9 Khoi nghiep 7 16 17 1 article
10 Lien he NULL 19 24 0 contact
11 Kinh doanh 10 20 21 1 contact
12 Ky thuat 10 22 23 1 contact

Before use property $scoped, I use method getNestedList() such as:

$cate = Cate::getNestedList();

And I have result:

Array ( [1] => Tin tức [2] => - Chính trị [5] => - - Trong nước [6] => - - Ngoài nước [3] => - Xã hội [4] => - Kinh tế [7] => Bài viết [8] => - Tài chính [9] => - Khởi nghiệp mới [10] => Liên hệ [11] => - Phòng kinh doanh [12] => - Phòng kỹ thuật )

After then, I set property $scoped = array('component'); And I have result:

Array ( )

Please tell me why? And please explain more about the property $scoped Thanks you for your support.

Methos76 commented 9 years ago

Hello JackieDo,

after a long search i found out the reason for this problem today. It isn't enought to declare the property $scoped in your class, but you should also declare the value to scope on.

like protected $scoped = array('component'); protected $comment = 'contact';

If you do so, you shouldn't get an empty array anymore.

If that is helpfull for you, good. ( May be you have to classes, one for the comments, another on for the articles ( according to your database column 'comment'.

My Problem seems to be unsolvable with the actual code, because I need to change the value the scope uses 'on the fly'.

In my case I have Categories, that are for different projects, but handled by one administration.

I changed the code of getNestedList this way:

public static function getNestedList($column, $key = null, $seperator = ' ', $scopedFieldsValues = array()) { $instance = new static;

$key = $key ?: $instance->getKeyName();
$depthColumn = $instance->getDepthColumnName();

foreach($scopedFieldsValues as $fieldName => $Value)
{
    $instance->scoped[] = $fieldName;
    $instance->$fieldName = $Value;
}
$nodes = $instance->newNestedSetQuery()->get()->toArray();

return array_combine(array_map(function($node) use($key) {
  return $node[$key];
}, $nodes), array_map(function($node) use($seperator, $depthColumn, $column) {
  return str_repeat($seperator, $node[$depthColumn]) . $node[$column];
}, $nodes));

}

} So I can give the wanted value for the scope into the function:

$x = Category::getNestedList('name', null, ' ', [ 'comment' => 'article'] );

surely not the best way to solve the problem, but for me it works at the moment

Greetings

JackieDo commented 9 years ago

Thank you and sorry for not replying sooner, because I'm busy with work. I highly agree and prefer your solution, it helped me a lot.

dhildreth commented 6 years ago

I hate to do this to a 3 year old bug, but could we maybe take a look at this again? Seems to me that the documentation for scoped support needs work.

Methos76 was correct in that specifying the scope name does return results. In my case, I have two category scopes, one for pages and one for products. In my Category class, I add these two lines:

protected $scoped = 'categorizable_type';
protected $categorizable_type = 'page';

In my PageController class, I'm attempting to call getNestedList() like so:

$page_categories = Category::getNestedList('name', 'id', '  ');

Now, this does return a list of categories which are scoped to page. However, when I go to do the same thing in my ProductController class, I'm going to want the scope set to products or else I'm going to end up with the same results as in my PageController.

Is there another way to declare what scope each controller should use? Is there another way besides modifying the getNestedList() function like Methos76 did? Perhaps specifying the scope before the call is made to getNestedList() like so?

$categorizable_type = 'product';
$product_categories = Category::getNestedList('name', 'id', '  ');

$categorizable_type = 'page';
$page_categories = Category::getNestedList('name', 'id', '  ');

Maybe I'm missing something simple, but any help would be appreciated.

Thanks, Derek

dhildreth commented 6 years ago

Okay, I found a solution that works for me for now. Instead of modifying the vendor code, I simply added a new function called getScopedNestedList to my Category class. This is what it all looks like:

app/Category.php

/**
 * Columns which restrict what we consider our Nested Set list
 *
 * @var array
 */
protected $scoped = ['categorizable_type'];

public static function getScopedNestedList($scope, $column, $key = null, $seperator = ' ') {
    $instance = new static;

    $key = $key ?: $instance->getKeyName();
    $depthColumn = $instance->getDepthColumnName();

    $instance->categorizable_type = $scope;

    $nodes = $instance->newNestedSetQuery()->get()->toArray();

    return array_combine(array_map(function($node) use($key) {
      return $node[$key];
    }, $nodes), array_map(function($node) use($seperator, $depthColumn, $column) {
      return str_repeat($seperator, $node[$depthColumn]) . $node[$column];
    }, $nodes));
}

app/Http/Controllers/PageController.php

$categories = Category::getScopedNestedList('page', 'name', 'id', '  ');
dd($categories);

app/Http/Controllers/ProductController.php

$categories = Category::getScopedNestedList('product', 'name', 'id', '  ');
dd($categories);

Hopefully this helps somebody else out. ;-)