Автор Тема: [РЕШЕНО] Рекурсионный вывод категорий  (Прочитано 1265 раз)

0 Пользователей и 1 Гость смотрят эту тему.

Оффлайн drakulitka

  • Опытный
  • ***
  • Сообщений: 80
  • Карма: 1
  • Zend Framework
[РЕШЕНО] Рекурсионный вывод категорий
« : Сентябрь 05, 2016, 11:41:22 »
Доброе время суток.

Подскажите пожалуйста как выводить дерево категорий по уровням из массива объектов (Doctrine)

Поля сущности: (по аналогии http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html#one-to-many-self-referencing

    
/**
     * @var \Doctrine\Common\Collections\Collection
     *
     * @ORM\OneToMany(targetEntity="Application\Entity\Category", mappedBy="parent", cascade={"remove"})
     */
    
private $children;

    
/**
     * @var \Application\Entity\Category
     *
     * @ORM\ManyToOne(targetEntity="Application\Entity\Category", inversedBy="children")
     * @ORM\JoinColumns({
     *   @ORM\JoinColumn(name="parent", referencedColumnName="id", nullable=true)
     * })
     */
    
private $parent;



В таблице все сохраняется нормально:

id    parent   name
1
	
null
	
  
Категория 1
2
	
null
	
  
Категория 2
3
	
1
	
  
ПодКатегория 3 к 1
4
	
2
	
  
ПодКатегория 4 к 2



Если перебирать foreach'ем то в каждой записи $category->getParent == null. (Возможно из-за того что это не просто поле, а ссылка на другую запись)
Если делать выборку в виде массива, то поле Parent вовсе отсутствует.
« Последнее редактирование: Сентябрь 13, 2016, 09:15:23 от drakulitka »

Оффлайн drakulitka

  • Опытный
  • ***
  • Сообщений: 80
  • Карма: 1
  • Zend Framework
Re: Рекурсионный вывод категорий
« Ответ #1 : Сентябрь 05, 2016, 18:09:51 »
Дошло до меня, что поле Parent - это не просто id родительской категории, через виртуальное поле Сhildren можно в обратку получить дочерние категории. Это огромный плюс, так как тут даже рекурсия не нужна.

Вот решение:

    
<?php foreach($categories as $category): ?>
        <?php if ($category->getParent() === null) : ?>
            <li><?= $category->getName() ?></li>
            <?php foreach($category->getChildren() as $children) {?>
                <li><?= $children->getName() ?></li>
            <?php }?>
        <?php endif; ?>
    <?php endforeach; ?>


Вот это действительно мощь!!!

Оффлайн Koc

  • Мастер
  • ****
  • Сообщений: 345
  • Карма: 23
  • Symfony2 infected
Re: [РЕШЕНО] Рекурсионный вывод категорий
« Ответ #2 : Сентябрь 06, 2016, 01:16:34 »
И сколько же запросов будет на подобной странице при 500 категориях, например?)

Оффлайн drakulitka

  • Опытный
  • ***
  • Сообщений: 80
  • Карма: 1
  • Zend Framework
Re: [РЕШЕНО] Рекурсионный вывод категорий
« Ответ #3 : Сентябрь 06, 2016, 09:24:57 »
Разве при foreach идут запросы к БД?
Я думал что один раз получил объекты со связями и манипулирую уже ими не обращаясь больше к базе  :o

Оффлайн Koc

  • Мастер
  • ****
  • Сообщений: 345
  • Карма: 23
  • Symfony2 infected
Re: [РЕШЕНО] Рекурсионный вывод категорий
« Ответ #4 : Сентябрь 06, 2016, 13:44:29 »
Посмотрите в профайлер

Оффлайн drakulitka

  • Опытный
  • ***
  • Сообщений: 80
  • Карма: 1
  • Zend Framework
Re: [РЕШЕНО] Рекурсионный вывод категорий
« Ответ #5 : Сентябрь 06, 2016, 15:03:17 »
Установил ZendDeveloperTools, действительно для каждой новой parent-категории производится один дополнительный запрос  :'(

Для большого количества категорий - это боль!

Как бороться? Рекурсия?

Оффлайн Koc

  • Мастер
  • ****
  • Сообщений: 345
  • Карма: 23
  • Symfony2 infected
Re: [РЕШЕНО] Рекурсионный вывод категорий
« Ответ #6 : Сентябрь 08, 2016, 10:45:09 »
Нужно за один запрос просто выгребсти все категории и построить структуру вида

$categoryTree = array()
foreach (
$categories as $category) {
    
$categoryTree[(int)$category->getParent()->getId()][] = $category;
}

// далее работать с $categoryTree[0]

foreach ($categoryTree[0] as $rootCategory) {
    if (isset(
$categoryTree[$rootCategory->getId()]) {
        foreach (
$categoryTree[$rootCategory->getId()] as $childCategory) {
            
// ... и так далее, если нужно - можно и рекурсивно
        
}
    }
}

Оффлайн drakulitka

  • Опытный
  • ***
  • Сообщений: 80
  • Карма: 1
  • Zend Framework
Re: [РЕШЕНО] Рекурсионный вывод категорий
« Ответ #7 : Сентябрь 10, 2016, 18:42:53 »

Fatal error
Uncaught TypeErrorArgument 1 passed to ZendDeveloperTools\Exception\SerializableException::__construct() must be an instance of Exceptioninstance of Error givencalled in /home/developer/www/zend-mvc.loc/vendor/zendframework/zend-developer-tools/src/Collector/ExceptionCollector.php on line 45 and defined in /home/developer/www/zend-mvc.loc/vendor/zendframework/zend-developer-tools/src/Exception/SerializableException.php on line 26


Не получается, уже тут ошибка:

$categoryTree = array()
foreach (
$categories as $category) {
    
$categoryTree[(int)$category->getParent()->getId()][] = $category;
}

« Последнее редактирование: Сентябрь 12, 2016, 09:11:11 от drakulitka »

Оффлайн Koc

  • Мастер
  • ****
  • Сообщений: 345
  • Карма: 23
  • Symfony2 infected
Re: Рекурсионный вывод категорий
« Ответ #8 : Сентябрь 12, 2016, 14:48:18 »
Как вариант - так попробовать. Вообще я примерно описал идею, нужно от нее плясать и уже отлаживать

$categoryTree = array()
foreach (
$categories as $category) {
    
$parentId $category->getParent() ? (int)$category->getParent()->getId() : 0;
    
$categoryTree[$parentId][] = $category;
}

Оффлайн drakulitka

  • Опытный
  • ***
  • Сообщений: 80
  • Карма: 1
  • Zend Framework
Re: Рекурсионный вывод категорий
« Ответ #9 : Сентябрь 13, 2016, 09:14:52 »
Огромнейшее спасибо, то что надо!
Всего один запрос идет к БД.
Надо

мб, еще кому-нибудь пригодится Ваше решение:


<?php
$categoryTree 
= [];
foreach (
$categories as $category) {
    
$parentId $category->getParent() ? (int)$category->getParent()->getId() : null;
    
$categoryTree[$parentId][] = $category;
?>


<?php foreach ($categoryTree[null] as $rootCategory) { ?>
    <div class="parent"><?= $rootCategory->getName(); ?>
    <?php if (isset($categoryTree[$rootCategory->getId()])) { ?>
        <?php foreach ($categoryTree[$rootCategory->getId()] as $childCategory) { ?>
            <div class="child"> - <?= $childCategory->getName(); ?></div>
            <?php ?>
    <?php ?>
    </div>
<?php ?>