Sonata: find out route names for generateUrl() in actions

August 13th, 2017

Within a controller I tried to redirect the user by return $this->redirect($this->generateUrl('...'));, but I couldn't find the corresponding route name. I did not define any baseroutepattern or baseroutename in the Admin file. In order to find out the route name, locate the service entry for the corresponding admin in admin.yml and copy the service name. On the terminal, type bin/console sonata:admin:explain and paste the service name to the end of the command. This command shows all routes associated with the service which can be used in generateUrl().

AsRock Z87 Pro4 + Samsung Evo 840 + Windows 10: Random Crashes

Juli 13th, 2017

After updating from Windows 7 to 10, the computer randomly hung after 5 to 10 minutes. Numlock on the keyboard still reacted, but the mouse froze and Ctrl-Alt-Del didn't do anything. Rebooting was only possible by cold reset. Running hwinfo reproducably made the machine hang on starting the program at scanning the drives. In the Windows logs there was nothing helpful. The CPU temperatures did not show anything unusual. I figured the issue was most likely driver- or firmware-related or due to a hardware defect.

I performed the following steps which were probably not successful or neccessary:

  • Cleaning CPU fans and heatsink
  • Updating drivers of the (very high-end) graphic card
  • Exchanging the graphic card
  • Updating the BIOS
  • Updating the SSD firmware

I suspect one or more of the following driver updates (Download at http://www.asrock.com/mb/Intel/Z87%20Pro4/?cat=Download&os=Win1064) to have contributed to solving the problem:

  • INF driver
  • Intel Rapid Storage Technology driver and utility
  • Intel Smart Connect driver
  • Intel Management Engine driver

Insight: QR Code Scanning for Event Invitations with Android

Juli 3rd, 2017

A customer requested a web application which allows to scan QR codes on invitations. It should run on mobile phones. The validity check is performed within a Navision system via a SOAP webservice. The most important requirement was a high-performant interface, because the customer has events with several thousands of guests.

I was first sceptical about the performance of the internal mobile phone cameras and the scan speed, so I recommended a hardware solution, connecting a professional QR code scanner to a mobile phone with an OTG adapter. We experimented a bit and found that the internal camera is completely sufficient if it is used together with a fast QR scan app.

A first approach was to capture the internal camera stream with HTML5 and recognize the QR code with a JavaScript library. I tried jsqrcode (https://github.com/LazarSoft/jsqrcode), but it was way too slow. The problem seems to be the autofocus which cannot be controlled with HTML5 in a quick and reliable way (yet). So we switched to a QR code scan app which implements a keyboard.

I created a form with just one text input for capturing the QR code. The user has to tap on this text input field to trigger the keyboard view. It is not possible on current mobile browsers to reliably show this keyboard on opening a page with JavaScript. On some older browsers there were some hacks that could do that. On the keyboard, the user has to choose the scan function of the keyboard app to start scanning. The text field autosubmits the content via JavaScript if no further key input arrives after 100ms. The webserver connects to the Navision webservice and returns the result back to the user. This solution is quick and robust enough for practical usage according to our tests.

Which apps are good for this application? We tried "Niko Barcode Keyboard" free and premium, and "Keyboard with Barcode/NFC Scanner" from TEC-IT. All three are perfectly usable for this case. The premium Niko Keyboard has a feature of a numeric keyboard on which the QR scan button is very large. This is a nice feature usability-wise. The TEC-IT scanner is a little bit faster though. It is so greedy that it even finds QR codes in the surroundings which were not meant to be scanned.

Why not directly talk to the webservice from the web application via JavaScript? I tried to avoid the round trip to the webserver to the Navision webservice and back. Because of the same origin policy this was not possible. It prevents from making AJAX calls to other domains than the one the script was initially loaded from. This would have been necessary for performing SOAP calls to the webservice. In this case, the web application had to reside on a different domain.

Sonata: Fill, save, and update multiple-entry field

Juli 2nd, 2017

I have an application which contains shipments and orderings. One shipment can have 0..N orderings. One ordering has maximum one shipment.

Within the ShipmentAdmin I configured the form fields as follows. They show all orderings without a shipment (1). If the current form already shows an existing shipment, we also need to show these shipments (2).

    protected function configureFormFields(FormMapper $formMapper)
    {
        $formMapper
            ...
            ->add('orderings','entity', 
              array(
                  'class'   => 'Lysano\ConnectBundle\Entity\Ordering',
                  'query_builder' => function 
                  (\Lysano\ConnectBundle\Repository\OrderingRepository $er) {
                    $qb = $er->createQueryBuilder('o');
                    $qb->where('o.shipment IS NULL'); // 1
                    if ($this->getSubject()->getId()) { // 2
                      $qb->orWhere('o.shipment = :shipm')
                          ->setParameter('shipm', $this->getSubject());
                    }
                      return $qb;
                  },
                  'multiple' => true
                  )
              )
       ;
    }

In order to save chosen orderings, I added prePersist() and preUpdate() to ShipmentAdmin. prePersist() iterates through all chosen orderings and updates them (3). preUpdate() needs to also care about deleted orderings (4).

    public function prePersist($object)
    {
        ...
        foreach ($object->getOrderings() as $ordering) { // 3
          $ordering->setShipment($object);
        }
   }

    public function preUpdate($object)
    {
        ...
        
        // delete old // 4
        $idArr = array(); // IDs to keep
        foreach ($object->getOrderings() as $ordering) {
          $idArr[] = $ordering->getId();
        }
        $em = $this->getModelManager()->getEntityManager($this->getClass());
        $qb = $em->createQueryBuilder();
        $qb->update('LysanoConnectBundle:Ordering', 'o')
                ->set('o.shipment', 'null')
                ->where('o.shipment = ?1');
        if (count($idArr) > 0) {
          $qb->andWhere($qb->expr()->notIn('o.id', $idArr));
        }
        $q = $qb->setParameter(1, $object->getId())
          ->getQuery();
        $q->execute();
        
        // add new:
        foreach ($object->getOrderings() as $ordering) {
          $ordering->setShipment($object);
        }
    }

MySQL dying and not restarting on Ubuntu 14.04.3 LTS

Juni 28th, 2017

I loaded an SQL dump via a web interface. It timed out. Afterwards the database server responded for any requested database 'SQLSTATE[HY000]: General error: 2006 MySQL server has gone away'. The MySQL process was still there though.

Problem 1: AppArmor
I could neither stop the process via service mysql stop nor start it via service mysql start. None of the commands returned. No error message, not on the command line, not in the logs. This is a symptom for AppArmor going wild. apparmor_status showed that mysql was in enforced mode, so it looked like it wasn't even allowed to start, which explains why there is no error in the logs. I added it to complain with aa-complain /etc/apparmor.d/*mysql*, which didn't change anything. So I deactivated apparmor testwise with service apparmor teardown. After that, mysql would at least log errors again while the start fails.

Problem 2: Changed default settings after update of MySQL to 5.5.x
The first error in the log (/var/log/mysql/error.log) was [ERROR] Failed to access directory for --secure-file-priv. I added into the section [mysqld] of my.cnf secure-file-priv = "" which solved this problem. MySQL starts as usual. I assumed that the SQL dump I was trying to load before wasn't read completely, so I re-read it from commandline via mysql < dump.sql. It loaded without error, but the MySQL server immediately went down again after connecting to the database. The log in /var/log/mysql/error.log said [ERROR] Cannot find or open table ... from the internal data dictionary of InnoDB though the .frm file for the table exists. This error was not related to the problem though: I found that with a version update of MySQL somewhere around 5.5.55 the default max_allowed_packet size decreased, so I set it in my.cnf within [mysqld] to max_allowed_packet = 32M. Voila, after a restart of MySQL everything worked again.

After a machine reboot, everything still works, so AppArmor seems to behave.