Archive for the ‘Symfony’ Category

Gedmo Softdeletable einfach aushebeln

05. August 2016

Gedmo Softdeletable hilft dabei, Datensätze nicht komplett aus der Datenbank zu löschen, sondern ein deletedAt zu setzen. Prinzipiell eine gute Sache, allerdings brauchte ich für eine bestimmte Spalte die Möglichkeit, die Funktionalität kurzzeitig auszuhebeln und die Datensätze tatsächlich aus der Datenbank zu löschen. Offenbar gibt es hier einen Trick, und zwar das deletedAt Feld direkt zu setzen mit new \DateTime:

  1. $em->setDeletedAt(new \DateTime());
  2. $em->remove($item);
  3. $em->flush();

Diesen Trick habe ich im Beitrag von Keksa ganz unten gefunden.

group_by im formbuilder: erstellen von optgroups im dropdown

11. Juni 2015

Ich habe 2 Entities, “Category” und “DetailCategory”. Jede DetailCategory hat eine Category. Um dem User das Finden der richtigen DetailCategory zu erleichtern (da sie oft auch gleich benannt sind), wollte ich nicht einfach ein Dropdown, sondern eines, wo die Categories optgroups sind. Ich fand heraus, dass das eigentlich mit group_by geht, also versuchte ich zunächst folgendes:

  1. $form = $this->createFormBuilder()
  2. ->add('detailCategories', 'entity', array('label' => 'Kategorien', 'multiple' => true, 'class' => 'Package\Bundle\Entity\DetailCategory', 'group_by' => 'category', 'property' => 'name'))
  3. ->getForm();

Da hat Symfony aber nicht mitgemacht und eine Fehlermeldung geworfen:

  1. Warning: Illegal offset type in isset or empty

Der Grund dafür ist wohl, dass Symfony mit “category” so nicht klarkommt, da es kein Feld sondern eine Entity ist. Also habe ich diesen hilfreichen Beitrag auf Stackoverflow gefunden und dementsprechend in meiner DetailCategory Entity eine Funktion hinzugefügt:

  1. public function getCategoryName(){
  2. if (null === $this->getCategory()) {
  3. return null;
  4. }
  5. if (null === $this->getCategory()->getName()) {
  6. return null;
  7. }
  8. return $this->getCategory()->getName();
  9. }

Und dann habe ich im Formbuilder das group_by entsprechend geändert:

  1. 'group_by' => 'categoryName'

Und tatsächlich, damit ging’s auf einmal!

Symfony2 und die Facebook php sdk 4

16. März 2015

Um in Symfony2 mit der neuen Facebook PHP SDK zu arbeiten, muss man zunächst die library über den composer hinzufügen. Man fügt also im composer.json unter require eine Zeile hinzu:

  1. "facebook/php-sdk-v4" : "4.0.*",

und dann den composer updaten lassen (“php composer.phar update”). Wenn alles geklappt hat, kann man nun in seinem Controller wenn benötigt oben die Facebook Klassen via “use” hinzufügen:

  1. use Facebook\FacebookSession;
  2. use Facebook\FacebookRedirectLoginHelper;
  3. use Facebook\FacebookRequest;
  4. use Facebook\FacebookResponse;
  5. use Facebook\FacebookSDKException;
  6. use Facebook\FacebookRequestException;
  7. use Facebook\FacebookAuthorizationException;
  8. use Facebook\GraphObject;

Und dann lassen sich die Klassen ohne Pfad einfach ansprechen, beispielsweise

  1. FacebookSession::setDefaultApplication($id, $secret);

Was ich dann festgestellt habe, ist dass Symfony mit dem Session Handling von Facebook so nicht klarkommt, irgendwann wirft es dann eine Exception:

  1. FacebookSDKException: Session not active, could not store state.

Dies lässt sich aber sehr einfach beheben, denn in Symfony2 ist die Session sowieso immer aktiv, ein Prüfen des Session Status braucht es nicht:

  1. $helper = new FacebookRedirectLoginHelper( $this->generateUrl( 'facebooklogin', array(), true ) );
  2. $helper->disableSessionStatusCheck();

Danke vielmals an den Stackoverflow Beitrag von SammyK, der die 3 optionen sehr gut beschreibt.

Symfony: cookies gehen im iframe verloren im Internet Explorer

21. Januar 2015

Ich habe ein Formular mit einem iframe als target. Die Url die dort aufgerufen wird ist die login_check von Symfony, der ich _username und _password schicke, in der Hoffnung, sofort eingeloggt zu werden. Problem: der Internet Explorer verliert die cookies im iframe. Also muss ich den entsprechenden Header setzen, und um das zu vereinfachen setze ich ihn einfach überall indem ich einen Listener einbaue:

  1. namespace Package\SomeBundle\EventListener;
  2. use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
  3. class ResponseListener{
  4. public function onKernelResponse(FilterResponseEvent $event){
  5. $event->getResponse()->headers->set('P3P', 'CP="IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT"');
  6. }
  7. }

Und registriert wird das ganze in der services.yml:

  1. user.filter_response_listener
  2. class: Package\SomeBundle\EventListener\ResponseListener
  3. tags:
  4. - {name: kernel.event_listener, event: kernel.response, method: onKernelResponse}

Symfony: neues Objekt mit Klassennamen aus einer Variable

13. Januar 2015

Ich habe eine Superklasse User, von der sich ein paar Subtypen ableiten:

  1. class Admin extends User { ... }
  2. class Redakteur extends User { ... }
  3. class Noob extends User { ... }

Im Controller beim Anlegen eines neuen Objektes weiß ich anhand der Auswahl schon, welches neue Objekt erstellt werden muss. Ich wollte das ganze aber nicht mit Switch bzw. einer if-Abfrage machen wollte. Meine verschiedenen Usertypen gibt es als Tabelle mit einem Usertyp pro Zeile. Der hat eine ID, einen Namen und, wichtig, eine Spalte “classname”.

classname

id name
1 Administrator Admin
2 Redakteur Redakteur
3 Darf gar nichts Noob

Und damit ich nun das neue Objekt anhand dieser classnames erstellen kann, benutze ich die ReflectionClass:

  1. $tmp2 = new \ReflectionClass('Pack\UserBundle\Entity\\'.$group->getClassname());
  2. $user = $tmp2->newInstance($exists);

Und $user ist damit vom jeweiligen Typ des $group Eintrags.

Symfony: im dev Modus das ewige assets:install vermeiden

12. Januar 2015

Früher habe ich nach jedem ändern von js und css Dateien immer ein assets:install ausgeführt, einfach weil ich es nicht besser wusste. Offenbar kann man das im dev modus ganz einfach via symlink umgehen:

  1. php ./console assets:install ../web --env=dev --symlink

Symfony/Twig: “Node “1” does not exist for Node “Twig_Node”

25. Juni 2014

Was ich tun wollte, war je nach Parameter ein twig template zu erweitern (extend) oder eben nicht. Hintergrund: ich wollte eine Seite 2x darstellen, einmal mit komplettem Layout und einmal für ein Overlay ohne. Aber als ich dies hier versuchte

  1. {% if type == 'page' %}
  2. {% extends 'Core::layout.html.twig' %}
  3. {% endif %}

kam von Symfony die Meldung “Node “1” does not exist for Node “Twig_Node”. Der Grund ist offenbar ein grundlegendes Problem mit Twig, es ist nihct möglich, ein extend in ein If einzubetten. Was allerdings geht ist folgendes:

  1. {% extends type == 'page' ? 'Core::layout.html.twig' : 'Core::empty.html.twig' %}

Ich musste mir extra die empty.html.twig erstellen, aber besser als für jede Seite die das können muss 2 Template zu erstellen.

Doctrine – IF-Abfrage in ORDERBY

09. Mai 2014

Das Problem: es gibt in der Datenbank im Feld ranking eine Rangliste nach der einzelne Einträge sortiert werden sollen. Allerdings soll das die Reihenfolge aufsteigend sein (also ORDER BY ranking ASC), ausgenommen die Zeilen, die NULL sind. Die sollen als letztes kommen.

Was ich nun eigentlich erreichen möchte ist eine Query dieser Art:

  1. SELECT * FROM table ORDER BY IF(ranking IS NULL, 9999, ranking) ASC

Ich habe nun auf alle mögliche Arten versucht, das mit Doctrine hinzubekommen, es ist aber immer fehlgeschlagen. Bis mir dieser freundliche Tipp gegeben wurde. Anscheinend kann Doctrine kein IF in OrderBy (wieso auch? ….), was es allerdings kann ist ein CASE WHEN im Select, das man dann im OrderBy referenzieren kann.

  1. $query = $this
  2. ->createQueryBuilder( 'a' )
  3. ->select('a')
  4. ->add('from', 'path\to\whatever\table a')
  5. ->addSelect('CASE WHEN a.ranking IS NULL THEN 9999 ELSE a.ranking END as HIDDEN ORD')
  6. ->where( 'a.deleted IS NULL' )
  7. ->orderBy( 'ORD', 'asc' )
  8. ->getQuery()
  9. ;

Und damit gehts! YAY!

Btw: doctrine is total bescheuert.

Symfony: eine Twig Extension erstellen

19. Dezember 2013

1. man erstellt eine Datei namens TheExtension.php die folgendermaßen aussieht:

  1. <?php
  2. namespace Something\Core\Extension\Twig;
  3. use Twig_Extension;
  4. use Twig_Filter_Method;
  5. class TheExtension extends Twig_Extension
  6. {
  7.   public function getFilters()
  8.   {
  9.     return array(
  10.         'cleanup' => new Twig_Filter_Method($this, 'cleanup')
  11.     );
  12.   }
  13.   public function cleanup($txt){
  14.     $txt = str_replace(array("ä", "ö", "ü", "Ä", "Ü", "Ö", "ß"), array("ae", "oe", "ue", "Ae", "Ue", "Oe", "ss"), $txt);
  15.     $txt = mb_strtolower($txt);
  16.     $txt = str_replace(" ", "_", $txt);
  17.     $txt = preg_replace('/[^A-Za-z0-9_]/', '', $txt);
  18.     $txt = preg_replace('/_{2,}/','_',$txt);
  19.     return $txt;
  20.   }
  21.   public function getName()
  22.   {
  23.     return 'the_extension';
  24.   }
  25. }

2. diese speichert man in seinem Projekt ab, z.B. unter src/Something/Core/Extension/Twig/TheExtension.php

3. in der config.yml definiert man diesen Service folgendermaßen:

  1. services:
  2.     core.twig.the_extension:
  3.       class: Something\Core\Extension\Twig\TheExtension
  4.       tags:
  5.           - { name: twig.extension }

Und dann kann man die Extension direkt in twig verwenden:

  1. {{ testvar|cleanup }}

Symfony: mehrere Controller in einem Bundle

30. August 2013

Ich hab ein Bundle namens ItemBundle und weil ein Item auch Kategorien hat, wollte ich neben dem ItemController auch einen CategoryController haben. Damit dieser Controller “erkannt” wird, muss man ihn anscheinend im routing.yml erwähnen…

_backend_item_category:
resource: “@PATHItemBundle/Controller/CategoryController.php”
type: annotation
prefix: /item/category