Fi1osof 24 марта 2013 0 1
Сегодня столкнулся с одной интересной задачей: мне надо было дважды подсчитать количество документов по условию и с группировкой данных. Вот запрос (запрос с искажениями, сокращен для лучшей читабельности):
<?php
// Формируем запрос
$q = $modx->newQuery('modResource');
$q->select(array( 'modResource.*'));

// Добавляем группировку
$q->groupby('parent');

// Считаем
$count = $modx->getCount('modResource', $q);

// Добавляем новую группировку
$q->groupby('pagetitle');

// Опять считаем
$count = $modx->getCount('modResource', $q);

Казалось бы, все замечательно, но давайте сформируем конечный SQL-запрос после второго метода группировки:
SELECT modResource.* FROM `modx_site_content` AS `modResource` GROUP BY parent, pagetitle

И вот здесь уже и кроется проблема. Метод xPDOQuery::groupby() не замещает поле, по которому надо сгруппировать, а добавляет его. То есть вместо того, чтобы сначала посчитать с группировкой по полю parent, а потом по полю pagetitle, после второго вызова ->groupby() у нас в условии группировки появилось два поля. А нам нужно только одно.

Конечно, если у нас запрос не большой, то можно и просто второй запрос накидать. А если запрос большой? Да еще и условия всякие? А еще и лень справедливо давит… В общем неплохо было бы просто объект $q скопировать в другую переменную, например $q2, и только после этого $q сгруппировать по parent и посчитать результат, и $q2 сгруппировать по pagetitle и посчитать результат с новым условием группировки. Тогда мы посчитаем количество записей в $q2, и не примешаем лишнюю группировку к объекту $q.

Давайте попробуем:
<?php
// Формируем запрос
$q = $modx->newQuery('modResource');
$q->select(array( 'modResource.*'));

// Выводим SQL для $q
$q->prepare();
print $q->toSQL();
print '<br /><br />'; 

// Копируем объект-переменную
$q2 = $q;

// Добавляем новую группировку
$q2->groupby('pagetitle');
 
// Выводим SQL для $q
$q2->prepare();
print $q2->toSQL();
print '<br /><br />'; 

// Добавляем группировку
$q->groupby('parent');

// Опять выводим SQL для $q
$q->prepare();
print $q->toSQL();
print '<br /><br />'; 

// Опять выводим SQL для $q2
$q2->prepare();
print $q2->toSQL();
print '<br /><br />'; 

То есть мы сформировали запрос, потом скопировали $q в переменную $q2, после чего уже добавили группировку отдельно для $q и отдельно для $q2. По идее мы должны для разных объектов получить различные условия группировки. Но что мы видим?
SELECT modResource.* FROM `modx_site_content` AS `modResource` 

SELECT modResource.* FROM `modx_site_content` AS `modResource` GROUP BY pagetitle 

SELECT modResource.* FROM `modx_site_content` AS `modResource` GROUP BY pagetitle, parent 

SELECT modResource.* FROM `modx_site_content` AS `modResource` GROUP BY pagetitle, parent 


То есть мы видим, что на выходе мы получили абсолютно одинаковые SQL-запросы для обоих объектов. Почему так произошло? Потому что в современном PHP при присвоении одного объекта из переменной другой переменной, объект не копируется (то есть не передается значение), а присваивается только ссылка на объект, и меняя любую из переменных, меняется единственный общий объект. В общем все почти как в Javascript. Так вот, для копирования (клонирования) объектов существует специальный операнд clone. Вот так будет выглядеть наш конечный запрос:
<?php
// Формируем запрос
$q = $modx->newQuery('modResource');
$q->select(array( 'modResource.*'));

// Выводим SQL для $q
$q->prepare();
print $q->toSQL();
print '<br /><br />'; 

// Копируем объект-переменную
$q2 = clone $q;

// Добавляем новую группировку
$q2->groupby('pagetitle');
 
// Выводим SQL для $q
$q2->prepare();
print $q2->toSQL();
print '<br /><br />'; 

// Добавляем группировку
$q->groupby('parent');

// Опять выводим SQL для $q
$q->prepare();
print $q->toSQL();
print '<br /><br />'; 

// Опять выводим SQL для $q2
$q2->prepare();
print $q2->toSQL();
print '<br /><br />'; 
Вот теперь мы получили уникальные SQL-запросы для каждого объекта в отдельности.
SELECT modResource.* FROM `modx_site_content` AS `modResource` 

SELECT modResource.* FROM `modx_site_content` AS `modResource` GROUP BY pagetitle 

SELECT modResource.* FROM `modx_site_content` AS `modResource` GROUP BY parent 

SELECT modResource.* FROM `modx_site_content` AS `modResource` GROUP BY pagetitle 

И теперь мы можем посчитать собственные результаты для этих запросов.

И напоследок кратко о методе __clone(); Если этот метод объявлен в классе, то он будет вызван на клонируемом объекте автоматически (именно на копии). Это может понадобиться для автоматического переопределения каких-либо свойств склонированного объекта.

Документация по clone: www.php.net/manual/ru/language.oop5.cloning.php
1 комментарий
abaddon651
abaddon65 28 марта 2013г в 20:53 #
А вот это очень познавательная вещь. Однозначно намотать на ус)
Авторизуйтесь или зарегистрируйтесь (можно через соцсети ), чтобы оставлять комментарии.