Nextras\Orm select DISTINCT v Mapperu

před 9 měsíci

TonnyVlcek
Člen | 27
+
0
-

Ahoj,

mám složitější dotaz na databázi a tak jsem ho dal dohromady v Mapperu. Dotaz mi funguje jen se mi prostě nedaří jej donutit generovat SELECT DISTINCT místo SELECT. Zkoušel jsem googlit, v kódu ORM jsem našel zajímavý trik, který měl naději na úspěch:

$builder = $this->builder()->select('DISTINCT [m.*]')->from('maker', 'm');

Když na konci mapper method dumpnu getQuerySql() se vše zdá ještě v pohodě:

bdump($builder->getQuerySql());
//SELECT DISTINCT [m.*] FROM maker AS [m] LEFT JOIN [maker_location] AS [l]....

Jenže jakmile si to přebere zbytek ORM tak se vygenruje tenhle dotaz

//SELECT COUNT(*) FROM (SELECT DISTINCT `m`.*, `m`.`id` FROM maker AS `m` LEFT JOIN ...
//a s ním i error
//Duplicate column name 'id'

Když jsem odebral countStored(), tak se dotaz rozchodil, ale pro změnu ten DISTINCT ignoruje úplně.

Jak donutit dotaz vygenerovaný v Mapperu používat DISTINCT?

MySql, Nextras ORM 2.2

před 9 měsíci

TonnyVlcek
Člen | 27
+
0
-

Ještě posílám kód dané metody, pro případ, že to pomůže :)

/**
     * Searches by various criteria
     *  name     -> name fragment
     *  location -> city or country
     *  from, to -> dates in different locations
     *  type     -> type of instruments made
     *
     * @param array $params
     * @return \Nextras\Dbal\QueryBuilder\QueryBuilder
     */
    public function advancedSearch(array $params)
    {
        $byName = $params['name'] ?? null;
        $byLocation = $params['location'] ?? null;
        $byDate = !empty($params['from']) || !empty($params['to']);
        $byType = $params['type'] ?? null;

        $builder = $this->builder()->from('maker', 'm');

        if ($byLocation || $byDate) {
            $builder = $builder->innerJoin('m', '[maker_location]', 'l', '[m.id] = [l.maker_id]');
            if ($byLocation) {
                $builder = $builder->andWhere('[l.city] LIKE %_like_ OR [l.country] LIKE %_like_', $byLocation, $byLocation);
            }

            if (!empty($params['from'])) {
                $builder = $builder->andWhere('[l.year_from] >= %i', (int) $params['from']);
            }

            if (!empty($params['to'])) {
                $builder = $builder->andWhere('[l.year_to] <= %i', (int) $params['to']);
            }
        }

        if ($byType) {
            $builder = $builder->innerJoin('m', '[maker_saledata]', 's', '[m.id] = [s.maker_id]')
                ->andWhere('type = %s', $byType);
        }

        if ($byName) {
            $builder->andWhere('firstname LIKE %_like_ OR lastname LIKE %_like_ OR middlename LIKE %_like_', $byName, $byName, $byName);
        }

        return $builder;
    }

před 9 měsíci

TonnyVlcek
Člen | 27
+
0
-

Tak vyřešeno :)

Nejspíš se to dá napsat hezčeji (pokud se k tomuhle někdy dostanete a napadne čistší řešení, tak budu rád, když se podělíte).

Nakonec jsem to přepsal tak aby to používalo Entity a generování dotazu jsem nechal na ORM. Problém byl v přidání OR podmínky, to jsem nakonec udělal takhle:

if (!empty($params['location'])) {
            call_user_func_array([$result->getQueryBuilder(), 'andWhere'], [
                '(%column LIKE %_like_ OR %column = %_like_)',
                'city', $params['location'],
                'country', $params['location'],
            ]);
        }

Kód celé metody pro referenci:

public function findMakersBy($params)
    {
        /** @var DbalCollection $result */
        $result = $result = $this->orm->makers->findAll();

        if (!empty($params['name'])) {
            $result = $this->orm->makers->searchByNameFragment($params['name']);
        }

        if (!empty($params['location'])) {
            call_user_func_array([$result->getQueryBuilder(), 'andWhere'], [
                '(%column LIKE %_like_ OR %column = %_like_)',
                'city', $params['location'],
                'country', $params['location'],
            ]);
        }

        if (!empty($params['from'])) {
            $result = $result->findBy(['this->locations->yearFrom>=' => $params['from']]);
        }

        if (!empty($params['to'])) {
            $result = $result->findBy(['this->locations->yearTo<=' => $params['to']]);
        }

        if (!empty($params['type'])) {
            $result = $result->findBy(['this->saledata->type' => $params['type']]);
        }

        return $result;
    }