Nextras\Orm select DISTINCT v Mapperu

TonnyVlcek
Člen | 31
+
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

TonnyVlcek
Člen | 31
+
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;
	}
TonnyVlcek
Člen | 31
+
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;
	}