Follow us on Twitter X-Cart on Facebook Wiki
Shopping cart software Solutions for online shops and malls

Extending Tags Module without Breaking Search

 
Reply
   X-Cart forums > X-Cart 5 > Dev Questions (X-Cart 5)
 
Thread Tools Search this Thread
  #1  
Old 07-10-2018, 09:03 AM
 
xgarb xgarb is offline
 

eXpert
  
Join Date: Jul 2004
Location: UK
Posts: 263
 

Default Extending Tags Module without Breaking Search

I've created a module that extends the Tags module. Basically it does site-wide filtering on grouped tags.

The problem is the code below is breaking the search - I guess as I'm decorating a class that search uses. How can I use my querybuilder code below without breaking search?




PHP Code:
<?php

class Product extends \XLite\Model\Repo\Product implements \XLite\Base\IDecorator
{

    protected function 
prepareCndSubstring(\Doctrine\ORM\QueryBuilder $queryBuilder$value)
    {
        
        
$tagsGroupsArray // not important but is something like  array('animal' => 'cat-dog','colour' => 'red-blue-green','size' => 'small')
        
        
$queryBuilder->linkLeft('p.tags''t'); 
        
$queryBuilder->linkLeft('t.tag_groups''tg');
        
        
$this->addTranslationJoins($queryBuilder't''tt'$this->getTranslationCode());
    
        
$queryBuilder->addGroupBy('p.id');
        
        foreach (
$tagsGroupsArray as $key => $val) {
            
$inString str_replace("-""','"$val);
            
$queryBuilder->andHaving("SUM(CASE WHEN tt.name IN ('".$inString."') AND tg.group_name = '".$key."' THEN 1 ELSE 0 END  ) > 0");
        }
            
}
__________________
Core version: 5.5.xx
Reply With Quote
  #2  
Old 07-11-2018, 01:30 AM
 
xgarb xgarb is offline
 

eXpert
  
Join Date: Jul 2004
Location: UK
Posts: 263
 

Default Re: Extending Tags Module without Breaking Search

OK.. I've done it like this....

PHP Code:
<?php 

class Product extends \XLite\Model\Repo\Product implements \XLite\Base\IDecorator 


    protected function 
prepareCndSubstring(\Doctrine\ORM\QueryBuilder $queryBuilder$value
    { 
    if (
$_GET['target'] != 'search'){         
        
$tagsGroupsArray // not important but is something like  array('animal' => 'cat-dog','colour' => 'red-blue-green','size' => 'small') 
         
        
$queryBuilder->linkLeft('p.tags''t');  
        
$queryBuilder->linkLeft('t.tag_groups''tg'); 
         
        
$this->addTranslationJoins($queryBuilder't''tt'$this->getTranslationCode()); 
     
        
$queryBuilder->addGroupBy('p.id'); 
         
        foreach (
$tagsGroupsArray as $key => $val) { 
            
$inString str_replace("-""','"$val); 
            
$queryBuilder->andHaving("SUM(CASE WHEN tt.name IN ('".$inString."') AND tg.group_name = '".$key."' THEN 1 ELSE 0 END  ) > 0"); 
        } 
         }
    if (
$_GET['target'] == 'search'){        
    
parent::prepareCndSubstring($queryBuilder$value);
    }            
}

The problem now is that getItemsCount() is counting all the products in the store while the page (the Itemlist) shows the correct result.

I don't know why getData() finds the products based on the tags correctly but it doesn't count the number of results.
__________________
Core version: 5.5.xx
Reply With Quote
  #3  
Old 07-11-2018, 05:48 AM
  tony_sologubov's Avatar 
tony_sologubov tony_sologubov is offline
 

X-Cart team
  
Join Date: Jan 2009
Posts: 2,431
 

Default Re: Extending Tags Module without Breaking Search

Hi @xgarb,

You do not need to change prepareCndSubstring() method in this case at all. You should define a new condition in your ItemsList, e.g. TagsSubstring and then add the handler of this condition to \XLite\Model\Repo\Product class.

See more about conditions here:
https://devs.x-cart.com/getting_started/working-with-database.html#method-search

See example of adding such condition to ItemsList here:
https://devs.x-cart.com/basics/itemslist_in_admin_area/

see how we define getSearchCondition() method there.

Please, let me know if it makes sense to you.

If it is unclear what I am trying to explain, please let me know. I will be happy to help.

Tony
__________________
Found a bug in X-Cart? Post it to our bug tracker!
Know how to make X-Cart better? Suggest an idea!
Reply With Quote

The following 2 users thank tony_sologubov for this useful post:
mcupka (07-11-2018), xgarb (07-12-2018)
  #4  
Old 07-12-2018, 06:51 AM
 
xgarb xgarb is offline
 

eXpert
  
Join Date: Jul 2004
Location: UK
Posts: 263
 

Default Re: Extending Tags Module without Breaking Search

Thanks for the tips. This is what I have so far...

Controller
PHP Code:
namespace XLite\Module\XCExample\SearchRepoDemo\Controller\Customer;
 
class 
ItemsListDemo extends \XLite\Controller\Customer\ACustomer
{
    public function 
getTitle()
    {
        return 
'Hello World';
    }


Repo
PHP Code:
namespace XLite\Module\XCExample\SearchRepoDemo\Model\Repo;

abstract class 
Product extends \XLite\Model\Repo\Product implements \XLite\Base\IDecorator
{
    const 
P_MORE_THAN_10 'moreThan10';
    const 
P_HAS_TAGS 'hasTags';
    
    protected function 
getHandlingSearchParams()
    {
        
$params parent::getHandlingSearchParams();

        
$params[] = self::P_MORE_THAN_10;
        
$params[] = self::P_HAS_TAGS;
        
        return 
$params;
    }    
    
    protected function 
prepareCndHasTags(\Doctrine\ORM\QueryBuilder $queryBuilder)
    {
        
$result $queryBuilder;    
        
        
$path 'approvals/atex-mcerts'//$value;
        
$tagsInUrl explode('/'$path);
        
        
$tagsGroupsArray = [];
        while (
count($tagsInUrl)) {
            
$tagsGroupsArray[array_shift($tagsInUrl)] = array_shift($tagsInUrl);
        }
            
        
$result->linkLeft('p.tags''t'); 
        
$result->linkLeft('t.tag_groups''tg');
        
        
$this->addTranslationJoins($result't''tt'$this->getTranslationCode());

        
$result->addGroupBy('p.id');
        
        
        foreach (
$tagsGroupsArray as $key => $val) {
            
$inString str_replace("-""','"$val);
            
$result->andHaving("SUM(CASE WHEN tt.name IN ('".$inString."') AND tg.group_name = '".$key."' THEN 1 ELSE 0 END  ) > 0");
        }
    
        return 
$result;
    }    
    
    protected function 
prepareCndMoreThan10(\Doctrine\ORM\QueryBuilder $queryBuilder$value)
    {
        
$result $queryBuilder;

        if (
$value) {
            
$result
                
->andWhere('p.price > :price')
                ->
setParameter('price'10.00);
        }

        return 
$result;
    }


Tag Model
PHP Code:
namespace XLite\Module\XCExample\SearchRepoDemo\Model;

class 
Tag extends \XLite\Module\XC\ProductTags\Model\Tag implements \XLite\Base\IDecorator
{

    
/**
     * 
     * @var \XLite\Module\XCExample\SearchRepoDemo\Model\TagGroup
     * @ManyToOne  (targetEntity="XLite\Module\XCExample\SearchRepoDemo\Model\TagGroup", inversedBy="tags")
     * @JoinColumn (name="group_id", referencedColumnName="id")     
     */
    
protected $tag_groups;    


Tag Group Model
PHP Code:
namespace XLite\Module\XCExample\SearchRepoDemo\Model;

/**
 * The "TagGroup" model class
 *
 * @Entity
 * @Table  (name="tag_groups")
 */
class TagGroup extends \XLite\Model\AEntity
{

    
/**
     * Unique ID
     *
     * @var integer
     *
     * @Id
     * @GeneratedValue (strategy="AUTO")
     * @Column         (type="integer", options={ "unsigned": true })
     */
    
protected $id;
     
    
/**
     * @Column (type="string", length=255)
     */
    
protected $group_name;


    
/**
     * Returns id
     *
     * @return string
     */
    
public function getId()
    {
        return 
$this->id;
    }    
        
    
/**
     * Set text
     *
     * @param string $value Value
     *
     * @return void
     */
    
public function setText($value)
    {
        
$this->text $value;
    }
    
    
/**
     * Returns text
     *
     * @return string
     */
    
public function getText()
    {
        return 
$this->text;
    }


ItemsList
PHP Code:
namespace XLite\Module\XCExample\SearchRepoDemo\View\ItemsList\Customer;

class 
ItemsListDemo extends \XLite\View\ItemsList\Product\Customer\Search
{
    const 
SORT_BY_MODE_PRICE 'p.price';

    protected function 
defineRepositoryName()
    {
        return 
'\XLite\Model\Product';
    }
    
    public static function 
getAllowedTargets()
    {
        return 
array_merge(parent::getAllowedTargets(), array('items_list_demo'));
    }
    

    public function 
__construct(array $params = array())
    {
        
$this->sortByModes += array(
            static::
SORT_BY_MODE_PRICE  => 'Price',
        );

        
parent::__construct($params);
    }

    protected function 
getSortByModeDefault()
    {
        return static::
SORT_BY_MODE_PRICE;
    }

    protected function 
getSearchCondition()
    {
        
$result parent::getSearchCondition();

        
$result->{\XLite\Model\Repo\Product::P_ORDER_BY} = $this->getOrderBy();
        
$result->moreThan10 true;
        
$result->hasTags true;

        return 
$result;
    }


I have the following error:

[Semantical Error] line 0, col 314 near 'tt WHERE p.enabled': Error: 'tt' is already defined.

from this code

PHP Code:
protected function prepareCndSubstring(\Doctrine\ORM\QueryBuilder $queryBuilder$value)
    {
        
$queryBuilder->linkLeft('p.tags''t');
        
$this->addTranslationJoins($queryBuilder't''tt'$this->getTranslationCode());

        
parent::prepareCndSubstring($queryBuilder$value);
    } 

in this file: XLite\Module\XC\ProductTags\Model\Repo\Product

I'm not sure what to do to stop the querybuilder adding the code in ProductTags repo?

T
__________________
Core version: 5.5.xx
Reply With Quote
  #5  
Old 07-13-2018, 07:14 AM
  tony_sologubov's Avatar 
tony_sologubov tony_sologubov is offline
 

X-Cart team
  
Join Date: Jan 2009
Posts: 2,431
 

Default Re: Extending Tags Module without Breaking Search

Hi @xgarb,

Perfect, seems like the mod is almost complete!
I do not think you need this 'moreThan10' condition though.

This error seems to be caused by a bug. The addTranslationJoins() method should have used linkLeft() method instead of leftJoin() one. I will report that.

As a workaround solution, try to add the following method to your \XLite\Module\XCExample\SearchRepoDemo\Model\Repo\ Product class

PHP Code:
protected function addTranslationJoins($queryBuilder$alias$translationsAlias$code)
    {
        
$queryBuilder->linkLeft(
            
$alias '.translations',
            
$translationsAlias
        
);

        return 
$queryBuilder;
    } 

Please, let me know if it fixes the problem.

Tony


Quote:
Originally Posted by xgarb
I have the following error:

[Semantical Error] line 0, col 314 near 'tt WHERE p.enabled': Error: 'tt' is already defined.

from this code

PHP Code:
protected function prepareCndSubstring(\Doctrine\ORM\QueryBuilder $queryBuilder$value)
    {
        
$queryBuilder->linkLeft('p.tags''t');
        
$this->addTranslationJoins($queryBuilder't''tt'$this->getTranslationCode());

        
parent::prepareCndSubstring($queryBuilder$value);
    } 

in this file: XLite\Module\XC\ProductTags\Model\Repo\Product

I'm not sure what to do to stop the querybuilder adding the code in ProductTags repo?

T
__________________
Found a bug in X-Cart? Post it to our bug tracker!
Know how to make X-Cart better? Suggest an idea!
Reply With Quote
  #6  
Old 07-13-2018, 08:02 AM
 
xgarb xgarb is offline
 

eXpert
  
Join Date: Jul 2004
Location: UK
Posts: 263
 

Default Re: Extending Tags Module without Breaking Search

That change gives me a 500 error with Allowed memory size of 268435456 bytes exhausted in the log.

I think it's something to do with this in my Product Repo..

$this->addTranslationJoins($result, 't', 'tt', $this->getTranslationCode());

and the same code in the prepareCndSubstring() method of the X-cart Tags module.

Due to both extending \XLite\Model\Repo\Product maybe.
__________________
Core version: 5.5.xx
Reply With Quote
  #7  
Old 07-16-2018, 03:47 AM
  tony_sologubov's Avatar 
tony_sologubov tony_sologubov is offline
 

X-Cart team
  
Join Date: Jan 2009
Posts: 2,431
 

Default Re: Extending Tags Module without Breaking Search

Hi @xgarb,

I tried the same solution on my X-Cart and it worked out properly. However, this error might be caused by the fact that MySQL query is too heavy and Doctrine cannot handle it properly on your server.

When you received this Allowed memory size of 268435456 bytes exhausted error, were there any hints about what might be causing it? Maybe some backtrace?

Tony

Quote:
Originally Posted by xgarb
That change gives me a 500 error with Allowed memory size of 268435456 bytes exhausted in the log.

I think it's something to do with this in my Product Repo..

$this->addTranslationJoins($result, 't', 'tt', $this->getTranslationCode());

and the same code in the prepareCndSubstring() method of the X-cart Tags module.

Due to both extending \XLite\Model\Repo\Product maybe.
__________________
Found a bug in X-Cart? Post it to our bug tracker!
Know how to make X-Cart better? Suggest an idea!
Reply With Quote
  #8  
Old 07-16-2018, 04:02 AM
 
xgarb xgarb is offline
 

eXpert
  
Join Date: Jul 2004
Location: UK
Posts: 263
 

Default Re: Extending Tags Module without Breaking Search

This is the X-cart PHP log...

Code:
<?php die(1); ?> [16-Jul-2018 12:56:35] Error (code: 1): Allowed memory size of 268435456 bytes exhausted (tried to allocate 20480 bytes) Server API: fpm-fcgi; Request method: GET; URI: /x5-tags-test/?target=items_list_demo; Backtrace: #0 Includes\ErrorHandler::logInfo() called at [/home/xxxx/public_html/x5-tags-test/Includes/ErrorHandler.php:403] #1 Includes\ErrorHandler::handleError() called at [/home/xxxx/public_html/x5-tags-test/Includes/ErrorHandler.php:388] #2 Includes\ErrorHandler::shutdown()

So not much help. The query was working when I was just over-riding the prepareCndSubstring but I didn't get the correct count value and as you've shown it's not really the right way to do this.

I could add some logging into the prepareCndSubstring() method of the X-cart Tags module to see what it's doing?
__________________
Core version: 5.5.xx
Reply With Quote
  #9  
Old 07-20-2018, 08:45 AM
 
xgarb xgarb is offline
 

eXpert
  
Join Date: Jul 2004
Location: UK
Posts: 263
 

Default Re: Extending Tags Module without Breaking Search

'K I have it nearly working again. The problem with the memory error turned out to be a problem with a duplicate file with a lowercase letter (Linux server, Windows workstation )

I've also solved the problem with the Error: 'tt' is already defined error with this code

PHP Code:
namespace XLite\Module\xxxx\xxxxProductTags\Model\Repo;

class 
Product extends \XLite\Model\Repo\Product implements \XLite\Base\IDecorator
{

    protected function 
prepareCndSubstring(\Doctrine\ORM\QueryBuilder $queryBuilder$value)
    {
        if (
$_GET['target'] == 'search'){        
            
parent::prepareCndSubstring($queryBuilder$value);
        }
    }
        


So the only problem is the items count is counting all products and not just the ones returned.

Using this prepared statement works fine. The items count is correct.

PHP Code:
protected function prepareCndMoreThan1000(\Doctrine\ORM\QueryBuilder $queryBuilder$value)
    {
    
        
$result $queryBuilder;

        if (
$value) {
            
$result
                
->andWhere('p.price > :price')
                ->
setParameter('price'1000);
        }

        return 
$result;
    } 

This prepared statement returns the correct items but the item count is wrong

PHP Code:
protected function prepareCndHasTags(\Doctrine\ORM\QueryBuilder $queryBuilder$value)
    {
        
$result $queryBuilder;    
        
        
$path 'approvals/atex-mcerts'// for test
        
$path = \XLite\Core\Request::getInstance()->substring;
        
$tagsInUrl explode('/'$path);
        
        
$tagsGroupsArray = [];
        while (
count($tagsInUrl)) {
            
$tagsGroupsArray[array_shift($tagsInUrl)] = array_shift($tagsInUrl);
        }
            
        
$result->linkLeft('p.tags''t'); 
        
$result->linkLeft('t.tag_groups''tg');
        
                
$this->addTranslationJoins($queryBuilder't''tt'$this->getTranslationCode());
        
        
$result->addGroupBy('p.id');
        
        
        foreach (
$tagsGroupsArray as $key => $val) {
            
$inString str_replace("-""','"$val);
            
$result->andHaving("SUM(CASE WHEN tt.name IN ('".$inString."') AND tg.group_name = '".$key."' THEN 1 ELSE 0 END  ) > 0");
        }
    
        return 
$result;
    } 

Is there some code in the statement that prevents the count function working?
__________________
Core version: 5.5.xx
Reply With Quote
  #10  
Old 07-23-2018, 06:24 AM
  tony_sologubov's Avatar 
tony_sologubov tony_sologubov is offline
 

X-Cart team
  
Join Date: Jan 2009
Posts: 2,431
 

Default Re: Extending Tags Module without Breaking Search

Hi @xgarb,

Quote:
Originally Posted by xgarb
Is there some code in the statement that prevents the count function working?

I do not see any problem with the code you have shown (assuming that your ItemsList class has proper getSearchCondition() method). Moreover, I tried to replicate similar module locally and it works well: no errors with counting.

You should probably dig into \XLite\View\ItemsList\AItemsList::getItemsCount() method to find out the real reason of this problem. Surely, before that you should disable widget's cache (Look & feel > Performance section in admin area).

Tony
__________________
Found a bug in X-Cart? Post it to our bug tracker!
Know how to make X-Cart better? Suggest an idea!
Reply With Quote
Reply
   X-Cart forums > X-Cart 5 > Dev Questions (X-Cart 5)


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Forum Jump


All times are GMT -8. The time now is 03:56 AM.

   

 
X-Cart forums © 2001-2020