Seiten: 1 2 3 4 5 6 7 8 9 10 11 ... 28 >>
When using a Doctrine query that contains something like ->select('SUM(u.stockchange) AS on_stock') in combination with a GROUP BY, the returned result might not be correct. Using this combination, I always got the complete sum over all u.stockchanges in the first row instead of the sum of the first partition defined by GROUP BY. Interestingly, this odd behaviour disappears when using an alias without underscore: replacing on_stock by onstock resolves the problem.
The situation: there is a "Product" table with a connected i18n table. The i18n table contains a column "name" and two languages: german and chinese.
Wanted: a product list created with the Admin Generator that allows for (1) sorting by name for both languages and (2) filtering by name. The final outcome should look and work like this:
(1) For the sorting:
generator.yml:
The Admin Generator should treat the i18n fields as if they were normal columns of the table. Specifically, they should be listed. For the columns to appear, a dedicated doSelectJoinTranslation table_method is needed as shown in the subsequent ProductTable.class.php.
config:
fields:
sortnamede: { is_real: true }
sortnamezh: { is_real: true }
list: display: [=sortnamede, =sortnamezh, eancode]
table_method: doSelectJoinTranslation
ProductTable.class.php:
The translation joins should happen without changing the result. When adding left joins in conjunction with "WHERE" on these joins, they become inner joins, not showing all rows of Product that do not yet have an i18n name given. To overcome this, the language selection goes into the join predicate. Furthermore, the selected names are given aliases (sortnamede, sortnamezh) as previously used in generator.yml.
public static function doSelectJoinTranslation($query) {
$rootAlias = $query->getRootAlias();
return $query->select($rootAlias.'.*, t.name as sortnamede, u.name as sortnamezh')
->leftJoin($rootAlias.'.Translation t ON ((t.lang = \'de\') AND t.id='.$rootAlias.'.id)')
->leftJoin($rootAlias.'.Translation u ON ((u.lang = \'zh\') AND u.id='.$rootAlias.'.id)');
}
actions.class.php:
The action allows for adding functionality to the default one. First, the valid sort columns are extended by the two "virtual" ones. Second, a sort query is added. Depending on the column to be sorted, it chooses for either the german translation "t" (as stated in the previous doSelectJoinTranslation) or "u" for the chinese one:
class productActions extends autoProductActions
{
protected function isValidSortColumn($column) {
return parent::isValidSortColumn($column) || $column == 'sortnamede' || $column == 'sortnamezh';
}
protected function addSortQuery($query) {
if (array(null, null) == ($sort = $this->getSort())) {
return;
}
if (!in_array(strtolower($sort[1]), array('asc', 'desc'))) {
$sort[1] = 'asc';
}
switch ($sort[0]) {
case 'sortnamede':
$sort[0] = 't.name';
break;
case 'sortnamezh':
$sort[0] = 'u.name';
break;
}
$query->addOrderBy($sort[0] . ' ' . $sort[1]);
}
[...]
(2) For the filtering:
ProductFormFilter.class.php:
First, there has to be added a "name" field, which is not included by default for i18n columns. Second, there has to be added a dedicated query for the search of the name. In order not to conflict with the other table aliases used above, there has to be chosen a new one ("s" in this example) for this purpose.
class ProductFormFilter extends BaseProductFormFilter
{
public function configure()
{
$this->widgetSchema['name'] = new sfWidgetFormFilterInput(array('with_empty' => false));
$this->validatorSchema['name'] = new sfValidatorPass(array('required' => false));
}
public function addNameColumnQuery(Doctrine_Query $query, $field, $values)
{
if (is_array($values) && isset($values['text']) && '' != $values['text'])
{
$query->leftJoin('r.Translation s')
->andWhere('s.name like ?', '%' . $values['text'] . '%');
}
}
}
That's it. ![]()
When deploying a new project to a production server, I got a strange error:
[?php /** * Project filter form base class. * * @package aaa * @subpackage filter * @author Your name here * @version SVN: $Id: sfDoctrineFormFilterBaseTemplate.php 23810 2009-11-12 11:07:44Z Kris.Wallsmith $ */ abstract class BaseFormFilterDoctrine extends sfFormFilterDoctrine { public function setup() { } }
Fatal error: Class 'BaseFormFilterDoctrine' not found in /srv/vhosts/aaa/www/lib/filter/doctrine/base/BaseStockFormFilter.class.php on line 11
I googled it, but did not find a working solution. The start of the error "[?php" is a hint: it is not a regular php file the autoloader tried to include here. And it is not the BaseFormFilterDoctrine.class.php either, because it contains my name instead of "Your name here". It's a template file either from plugins/sfDoctrineGuardPlugin or from the kernel.
Hence, the autoloader has a hickup that only occurs in the presence of the php configuration on the production server. It can be solved by hacking the symfony kernel. I added "template, test" to the exclude entry of /lib/vendor/symfony/lib/config/config/autoload.yml - and voila, symfony loads the correct BaseFormFilterDoctrine.
Experiment abgebrochen: Ich wollte ein neues Projekt auf Symfony 2 aufsetzen. Als Anhänger von Symfony 1 halte ich das aber für ein zu großes Wagnis: Es fehlen zentrale Eigenschaften, wie z. B. der Admin Generator, die nur unter aus meiner Sicht zu hohem Aufwand als externes Modul nachinstalliert werden können und schlecht dokumentiert sind. Vorhandenen Code aus Symfony-1-Projekten kann man aufgrund der Rundumerneuerung kaum weiterverwenden. Das erhöht die Entwicklungszeit und Fehleranfälligkeit deutlich. Daumen runter - ich bleibe bei Symfony 1. Wenn noch jemand Lust hat auf einen Fork nach dem Lebensende von 1.4 dieses Jahr - ich bin dabei.
Um von Japan nach China zu kommen, haben wir eine sehr ... preisgünstige Fluglinie gebucht: So fliegt sie nicht wie alle anderen von Narita oder Haneda, sondern von dem ehemaligen reinen Militärflughafen Ibaraki. Er ist etwas über zwei Stunden mit dem Bus von Tokyo entfernt. Laut Wikipedia wurde er unter Zugabe von viel Subventionen geplant als billige Alternative zu den vorhandenen Flughäfen und spart z. B. an Wagen, die Flugzeuge wegziehen, indem Flugzeuge einfach schräg ans Terminal heranfahren und damit selber wieder wegfahren können. Zusteigen kann man nur per Treppe von draußen. Den Bus dorthin sollte man rechtzeitig reservieren, damit man einen zur optimalen Zeit für den Flug bekommt. Er kostet für Fluggäste nur 500 Yen, was für die lange Fahrt extrem billig ist. Der Flughafen ist ulkig: Es fliegen am Tag ca. drei Maschinen ab, zwei davon innerhalb von Japan. Und man kann auf dem Aussichtspunkt Übungen mit Militärflugzeugen beobachten. Der Check-In war kurios: Die Zuteilung der Plätze erfolgte per manueller Eintragung auf einer Tapete, zu der alle Bediensteten am Check-In immer jeweils hingehen mussten. Die Gewichtsbeschränkung, die sich wohl auf Aufgabe- und Handgepäck bezieht, wurde per Bleistift und Papier kontrolliert. Es existiert kein Gepäckband. Man muss nach dem Check-In sein Gepäck an einem anderen Schalter aufgeben. Essen und Trinken gab es an Bord nur gegen Einwurf von Münzen. Gegen Ende des Flugs gab es einen speziellen Service: Leibesübungen. Die Stewardessen stellten sich in gewohnter Sicherheits-Demo hin und turnten vor.
Eigentlich sollten wir am Flughafen Shanghai-Pudong landen, der mit dem Transrapid mit Shanghai verbunden ist. Beim Landeanflug kam die Durchsage, dass wir heute ausnahmsweise an einem anderen Flughafen landen. Wenn man was erleben will, nehme man diese Airline. ;-)