phpmysqlezpublish

ezpublish 5.4 sort according to parent field


I am using ezpublish Search API to get the list of session content type objects ordered by their parent's training field (field name is code).

So if I have two sessions S1 (linked to training with code T1) and S2 (linked to training with code T2) I want to have session S1 listed before S2 in the list.

$criterions = array (
            new Criterion\ContentTypeIdentifier( 'session' )
        );
$locationQuery = new LocationQuery();
        $locationQuery->query = $criteriaArray;
        $locationQuery->sortClauses = array(
            //new SortClause\Field('session','price'),<- this works
            new SortClause\Field('training','code',Query::SORT_ASC)
           //this second sorClause does not work as code is not a field of session while price is
            );

There might be a solution to this problem using native mysql query but it's not portable and some ids will be hardcoded.

My questions is can we achieve a sort of the sesssions according to their parent's training'code`field ?


Solution

  • I managed to get it working thanks to @Edi and a colleague at my job.

    The two clasess to create are : The field class:

    class ParentSortClauseField extends SortClause
    {
    
        public function __construct($typeIdentifier, $fieldIdentifier, $sortDirection = Query::SORT_ASC)
        {
            parent::__construct(
                'field',
                $sortDirection,
                new FieldTarget($typeIdentifier, $fieldIdentifier)
            );
        }
    
    }
    

    The handler class:

    class ParentSortClauseHandler extends SortClauseHandler
    {
    
        protected $contentTypeHandler;
    
    
        public function __construct(
            DatabaseHandler $dbHandler,
            ContentTypeHandler $contentTypeHandler
        )
        {
            $this->contentTypeHandler = $contentTypeHandler;
    
            parent::__construct($dbHandler);
        }
    
    
        public function accept(SortClause $sortClause)
        {
            return $sortClause instanceof ParentSortClauseField;
        }
    
    
        public function applySelect(SelectQuery $query, SortClause $sortClause, $number)
        {
            $query
                ->select(
                    $query->alias(
                        $query->expr->not(
                            $query->expr->isNull(
                                $this->dbHandler->quoteColumn(
                                    'sort_key_int',
                                    $this->getSortTableName($number)
                                )
                            )
                        ),
                        $column1 = $this->getSortColumnName($number . '_null')
                    ),
                    $query->alias(
                        $query->expr->not(
                            $query->expr->isNull(
                                $this->dbHandler->quoteColumn(
                                    'sort_key_string',
                                    $this->getSortTableName($number)
                                )
                            )
                        ),
                        $column2 = $this->getSortColumnName($number . '_bis_null')
                    ),
                    $query->alias(
                        $this->dbHandler->quoteColumn(
                            'sort_key_int',
                            $this->getSortTableName($number)
                        ),
                        $column3 = $this->getSortColumnName($number)
                    ),
                    $query->alias(
                        $this->dbHandler->quoteColumn(
                            'sort_key_string',
                            $this->getSortTableName($number)
                        ),
                        $column4 = $this->getSortColumnName($number . '_bis')
                    )
                );
    
            return array($column1, $column2, $column3, $column4);
        }
    
        public function applyJoin(
            SelectQuery $query,
            SortClause $sortClause,
            $number,
            array $languageSettings
        )
        {
    
            /** @var \eZ\Publish\API\Repository\Values\Content\Query\SortClause\Target\FieldTarget $fieldTarget */
    
            $fieldTarget = $sortClause->targetData;
            $fieldMap = $this->contentTypeHandler->getSearchableFieldMap();
            if (!isset($fieldMap[$fieldTarget->typeIdentifier][$fieldTarget->fieldIdentifier]['field_definition_id'])) {
                throw new \InvalidArgumentException(
                    '$sortClause->targetData',
                    'No searchable fields found for the given sort clause target ' .
                    "'{$fieldTarget->fieldIdentifier}' on '{$fieldTarget->typeIdentifier}'."
                );
            }
            $fieldDefinitionId = $fieldMap[$fieldTarget->typeIdentifier][$fieldTarget->fieldIdentifier]['field_definition_id'];
    
            $table = $this->getSortTableName($number);
            $query
                ->innerJoin(
                    $query->alias('ezcontentobject_tree', 'parent')
                    ,
                    $query->expr->eq(
                        $this->dbHandler->quoteColumn('parent_node_id', 'ezcontentobject_tree'),
                        $this->dbHandler->quoteColumn('node_id', 'parent')
                    )
                );
            $query
                ->leftJoin(
                    $query->alias(
                        $this->dbHandler->quoteTable('ezcontentobject_attribute'),
                        $this->dbHandler->quoteIdentifier($table)
                    ),
                    $query->expr->lAnd(
                        $query->expr->eq(
                            $query->bindValue($fieldDefinitionId, null, PDO::PARAM_INT),
                            $this->dbHandler->quoteColumn('contentclassattribute_id', $table)
                        ),
                        $query->expr->eq(
                            $this->dbHandler->quoteColumn('contentobject_id', $table),
                            $this->dbHandler->quoteColumn('contentobject_id', 'parent')
                        ),
                        $query->expr->eq(
                            $this->dbHandler->quoteColumn('version', $table),
                            $this->dbHandler->quoteColumn('contentobject_version', 'parent')
                        ),
                        $query->expr->eq(
                            $this->dbHandler->quoteColumn('language_code', $table),
                            $query->bindValue('fre-FR')
                        )
                    )
                );
        }
    }
    

    Declare the handler as a service:

    app.sort_clause_handler.location.parent:
                class: ...\SortClauseHandler\ParentSortClauseHandler
                arguments:
                    - @ezpublish.api.storage_engine.legacy.dbhandler
                    - @ezpublish.spi.persistence.content_type_handler
                tags:
                    - {name: ezpublish.search.legacy.gateway.sort_clause_handler.content}
                    - {name: ezpublish.search.legacy.gateway.sort_clause_handler.location}
    

    And To call the pieces together in the cotroller I did this:

    $sortQuery[] = new ParentSortClauseField('parentclassname', 'field', $sortOrder);