ezpublish 5.4 sort according to parent field

229 views Asked by At

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 ?

2

There are 2 answers

0
zizoujab On BEST ANSWER

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);
2
Edi Modrić On

This is not possible unfortunatelly with the built in sort clauses.

What you can do is fetch trainings sorted by the code field and then for each training load its sessions separately. This will not be efficient, ofcourse, if you have many content objects in your database.

Another possibility might be to implement a custom sort clause that might do the trick.