Search code examples
mysqldoctrine-ormdoctrinedql

Doctrine with MySQL, Can I select all products within parent and sub categories?


I have done a bit off searching around for an answer, but most results are either not clear enough or I find it hard to implement in my current pattern... What I wish to achieve is having a query to select all products from the products table matching a category ID from the category table, But now i wish to also get products that are sub categories of the said parent category. I am using Doctrine 2 with codeigniter and my function so far looks like this

function searchForProducts( $offset, $limit ) {
    $search = $this->input->get('search');
    $category = (int)$this->input->get('category');
    $supplier = (int)$this->input->get('supplier');

    for( $i = 0; $i < 2; $i++ ) {
        $select = ($i == 0) ? 'count(p)' : 'p';
        $qb = $this->em->createQueryBuilder();
        $qb ->select($select)
            ->from(self::ENTITY, 'p');

        if( $i != 0) {
            $qb ->setFirstResult( (int)$offset )
                ->setMaxResults( (int)$limit );
        }

        if( $search ) {
            $qb ->where( "p.title LIKE :search" )
                ->orWhere( "p.sku LIKE :search" )
                ->setParameter('search', "%$search%");
        }

        if( $category ) {
            $qb ->andWhere( "p.category = ?1" )
                ->setParameter(1, $category);
        }

        if( $supplier ) {
            $qb ->andWhere( "p.supplier = ?2" )
                ->setParameter(2, $supplier);
        }

        if( $i == 0 ) {
            $this->totalRows =  $qb->getQuery()->getSingleScalarResult();
        } else {
            return $qb->getQuery()->getResult();
        }
    }   
}

I also don't think it would be practical to get all products then do it from application level as I'm using pagination and products could become quite large.


Solution

  • To answer my own question, I decided to take the easy way out, and considering i would only have around 40 categories max, i don't think this should be much of a problem.

    In my categories model class i created 2 static methods,

    static function getCategoryWithChildren( $id = NULL ) {
        $obj = new self;
        $parent = $obj->getCategory( $id );
        $list = array();
        self::iterateCategoriesChildren($parent, $list);
        return array_reverse($list);    
    }
    
    static function iterateCategoriesChildren( $category, &$array ) {
        if( $category->hasChildren() ) {
            foreach( $category->getChildren() as $child ) {
                self::iterateCategoriesChildren($child, $array);                
            }
        }
        $array[] = $category;
    }
    

    So pretty much this will get the parent and iterate all the children and assign each 1 to the flat array.

    Then from my searchProducts method i just added a bit more under the $category check.

            if( $category ) {
                if( $this->settings->get('product_child_categories') ) {
                    $categories = Categories_model::getCategoryWithChildren($category);
                    foreach($categories as $_category) {
                        $qb ->orWhere( "p.category = " . $_category->getID() );
                    }                   
                } else {                
                    $qb ->andWhere( "p.category = ?1" )
                        ->setParameter(1, $category);
                }               
            }
    

    May not be best or practical, but it works and does what I need for the time being.