Nextras\Orm select DISTINCT v Mapperu
- TonnyVlcek
- Člen | 31
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
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
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;
}