zf3 – get the route match in a custom view helper

Martin Zellerphp, zf3 2 Comments

In a very old post I told you how to get the route name in custom view helper with zf2 – Zend Framework 2. In this post I used the ServiceLocator, a thing which was some kind of deprecated in later versions of zf2. It is bad practice to use such a construct like the getServiceLocator method, because e.g. applications became very bad to unit test – you can find a lot of articles regarding this in the net.

With later zf2 versions and zf3 there are better ways to get the route name in your view helper. One way is to make use of ViewHelperProviderInterface in your src/Module.php class:

<?php

namespace Application;

use Application\View\Helper\YourViewHelper;
use Zend\ModuleManager\Feature\ViewHelperProviderInterface;
use Zend\ServiceManager\ServiceManager;

/**
 * Class Module
 * @package Application
 */
class Module implements ViewHelperProviderInterface
{
    /**
     * Expected to return \Zend\ServiceManager\Config object or array to
     * seed such an object.
     *
     * @return array|\Zend\ServiceManager\Config
     */
    public function getViewHelperConfig()
    {
        return [
            'factories' => [
                'yourViewHelper' => function(ServiceManager $serviceManager) {
                    return new YourViewHelper($serviceManager->get('Application')->getMvcEvent()->getRouteMatch()); // <---- this is the important part
                }
            ],
        ];
    }
}

That’s all. The basic code of the YourViewHelper view helper could look like this:

<?php

namespace Application\View\Helper;


use Zend\Router\Http\RouteMatch;
use Zend\View\Helper\AbstractHelper;


/**
 * Class YourViewHelper
 * @package Application\View\Helper
 */
class YourViewHelper extends AbstractHelper
{

    /**
     * @var RouteMatch
     */
    private $routeMatch;

    /**
     * YourViewHelper constructor.
     * @param RouteMatch $routeMatch
     */
    public function __construct(RouteMatch $routeMatch) {
        $this->routeMatch = $routeMatch;
    }

    /**
     * @return string
     */
    public function __invoke()
    {

        $route = $this->getRouteName();

        // ... do something with your view helper

    }

   /**
     * @return RouteMatch
     */
    protected function getRouteMatch()
    {
       return $this->routeMatch;
    }

    /**
     * @return string|null
     */
    protected function getRouteName()
    {
        return $this->getRouteMatch()->getMatchedRouteName();
    }

    /**
     * @param $param
     * @return mixed|null
     */
    protected function getRouteParam($param)
    {
        return $this->getRouteMatch()->getParam($param, '');
    }

}

Have fun.

Doctrine with zf3 cache

Martin Zellerdoctrine, php, zf3 Leave a Comment

That’s the way how to use Zend Cache with Doctrine in Zend Framework 3.

First do all doctrine settings and define a cache in your global.php:

'doctrine' => [
    'connection' => [
        // default connection name
        'orm_default' => [
            'driverClass' => 'Doctrine\DBAL\Driver\PDOMySql\Driver',
            'params' => [
                'host' => 'localhost',
                'port' => 3306,
                'user' => 'xxx',
                'password' => 'xxx',
                'dbname' => 'xxx',
                'charset' => 'utf8'
            ]
        ]
    ],
    'configuration' => [
        'orm_default' => [
            'metadata_cache'   => 'zend.dbcache',
            'query_cache'     => 'zend.dbcache',
            'result_cache'    => 'zend.dbcache',
        ]
    ],
],

'caches' => [
    'zend.dbcache' => [
        'adapter' => [
            'name' => 'filesystem',
            'options' => [
                'dirLevel' => 2,
                'cacheDir' => './data/cache/db',
                'dirPermission' => 0775,
                'filePermission' => 0666,
                'namespaceSeparator' => '-',
                'keyPattern' => '/^[\[\]\$\\a-z0-9_\+\-]*$/Di',
            ],
        ],
        'plugins' => [
            'exception_handler' => [
                'throw_exceptions' => false
            ],
            'serializer'
        ]
    ],

],

 

Then define a service ‘doctrine.cache.zend.dbcache‘ in your module/Application/src/Module.php:

/**
 * Expected to return \Zend\ServiceManager\Config object or array to
 * seed such an object.
 *
 * @return array|\Zend\ServiceManager\Config
 */
public function getServiceConfig()
{
    return [
        'factories' => [
            'doctrine.cache.zend.dbcache' => function(ServiceManager $serviceManager) {
                return new \DoctrineModule\Cache\ZendStorageCache($serviceManager->get('zend.dbcache'));
            }
        ],
    ];
}

openHAB Raspberry Pi Arduino XBee Led Zeppelin Music Machine

Martin ZellerArduino, openHAB, Raspberry Pi 1 Comment

For many years my daughter called me in the evening in her room: “Daddy, please play the barrel organ!”

Barrel Organ / Drehorgel

I usually asked: “How fast?”
And she replied: “Full speed!”
Okay.
After about ten years – my hands have become old and tired – I knew that I need a machine. I spoke to my daughter and we began to make plans.
After hours of thinking  we built a prototype with some toys:

 

And this is the funny result!
Attention: first you will hear nothing (or some noise from my stomach). Only when I touch my smartphone, you will hear LED ZEPPELIN! Yeah!

Here are some detailed images:

 

The ingredients for our openHAB Raspberry Pi Arduino XBee Led Zeppelin Music Machine:

  • HABDroid running on a Samsung Galaxy smartphone
  • a Raspberry Pi
    • with a XBee USB dongle
    • the openHAB runtime (extended with a custom XBee Binding)
  • an Arduino UNO
    • with a motor shield
    • a wireless shield (with a XBee module fitted)
  • an electric motor
  • a barrel organ

The sequence of events:

  1. my finger pushes the “Led Zeppelin on/off” switch on the HABDroid app on my smartphone
  2. HABDroid sends the event over my WLAN to the openHAB runtime running on the Raspberry Pi
  3. my openHAB binding receives the event, connects to the XBee USB dongle and sends “1” (0x31)
  4. my Arduino UNO receives the “1” over the XBee receiver item on the Wireless Shield
  5. my Arduino UNO sends “full speed” to the connected electronic motor
  6. the electronic motor starts and drives the barrel organ

Here is a very basic list of what you have to do if you want to build the same crazy thing:

  1. Install openHAB on a Raspberry Pi (must be connected to a LAN because we want to use HABDroid on a smartphone to control the openHAB runtime) – s. notes for openHAB
  2. Plug a XBee USB dongle into the Raspberry Pi – s. notes for XBee
  3. Do some Linux configuration on your Raspberry Pi – I use Raspbian – s. notes for Raspbian
  4. Configure your openHAB runtime to provide a switch for HABDroid – s. notes for openHAB
  5. Install a Java IDE for coding your custom XBee openHAB Binding for sending data over the XBee USB dongle and export it to your openHAB installation – s. notes for openHAB
  6. Place your Arduino UNO with a Motor Shield and a Wireless Shield (with a XBee module fitted)
  7. Install the Arduino IDE to create the “XBee” code and upload it to your Arduino – s. notes for the Arduino UNO
  8. Find a solution for how you can connect your Motor Shield to an electric motor and finally to the barrel organ.
  9. Think like a hobbyist and leave your family for two weeks to get it work!

Notes for Xbee:

For wireless commication between Raspberry Pi and the Arduino UNO we use two XBee modules. One as ZigBee Coordinator, the other one as ZigBee Router, both in API mode.
TIP: if you want to configure and test the XBee modules in a comfortable way, do it with the XCTU GUI! Here’s a screenshot:

XCTU Screenshot

Notes for Raspbian (or the OS which you run on your Raspberry Pi):

  1. Install Rxtx: sudo apt-get install librxtx-java – this installs librxtxSerial.so into /usr/lib/jni which we need for our XBee communication
  2. find our where your XBee USB dongle is mounted. In my case it is: /dev/ttyUSB0
  3. go to /dev and find out the group name of your XBee USB dongle. In my case it’s the group dialout
  4. add your openhab user to the dialout group

Notes for openHAB:

Edit the openHAB start scripts on your Raspberry Pi. In my case: /usr/share/openhab/bin/openhab.sh
You’ll find a part like this one:

[plain] …
JAVA_ARGS_DEFAULT="-Dosgi.clean=true \
-Dgnu.io.rxtx.SerialPorts=/dev/ttyUSB0 \
-Declipse.ignoreApp=true \
-Dosgi.noShutdown=true \
-Djetty.port=${HTTP_PORT} \
-Dopenhab.configfile="${OPENHAB_CONF_DIR}/configurations/openhab.cfg" \
-Dopenhab.configdir="${OPENHAB_CONF_DIR}/configurations" \
-Dopenhab.logdir="${OPENHAB_LOG_DIR}" \
-Dsmarthome.userdata="${OPENHAB_USER_DATA_DIR}"
-Djetty.home="${OPENHAB_DIR}" \
-Djetty.port.ssl=${HTTPS_PORT} \
-Djetty.config="${OPENHAB_CONF_DIR}/jetty" \
-Djetty.logs="${OPENHAB_LOG_DIR}" \
-Djetty.rundir="${OPENHAB_DIR}" \
-Dfelix.fileinstall.dir="${OPENHAB_DIR}/addons" \
-Dfelix.fileinstall.filter=.*\\.jar \
-Djava.library.path="${OPENHAB_DIR}/lib:/usr/lib/jni" \

[/plain]
  1. I added the line starting with “-Dgnu.io.rxtx.SerialPorts” (don’t forget to use the name of your XBee USB dongle mounting)
  2. and I extended the java.library.path parameter with the /usr/lib/jni path (s. below Rxtx installation)

The sitemap configuration:

[plain] Frame label="Music Machine" {
Switch item=MusicMachineSwitch label="Led Zeppelin on/off"
}
[/plain]

My items configuration:

[plain] Switch MusicMachineSwitch "Xbee Music Machine Switch" { mzxbee }
[/plain]

My setting for the openhab.cfg:
These are some configurations, which I use in my Java code. I want to communicate with 9600 baud, the mounting point for my Xbee USB dongle is /dev/ttyUSB0 and the MAC address of the receiver is “00 13 A2 00 40 A2 5A EC

[plain] mzxbee:baud=9600
mzxbee:comPort=/dev/ttyUSB0
mzxbee:address=0013A20040A25AEC
[/plain]

For our custom XBee openHAB Binding we use this Java API:
XBee Java Library

Here’s the important part of the Java code:

[java]logger.debug("Instantiating device with comPort " + comPort + " and " + baud + " baud…");
XBeeDevice myDevice = new XBeeDevice(comPort, baud);

logger.debug("Opening device…");
myDevice.open();

logger.debug("Instantiating address…");
XBee64BitAddress addr64 = new XBee64BitAddress(address);

logger.debug("Instantiating RemoteXBeeDevice device…");
RemoteXBeeDevice myRemoteXBeeDevice = new RemoteXBeeDevice(myDevice, addr64);

logger.debug("********************* Sending sync data: " + value + " > byte value: " + Character.digit(value,16));
myDevice.sendData(myRemoteXBeeDevice, new byte[]{ (byte)value });

logger.debug("Closing device…");
myDevice.close();[/java]

value can be 1 to turn the music machine on and 0 to turn it off.

Notes for the Arduino UNO

This is the code for the Arduino UNO. I want to let a LED blink, when the Arduino starts. The same LED shall light, when the music plays.

[c] #include <XBee.h>

int incomingByte = 0; // for incoming serial data
int dirA = 12;
int brake = 9;
int mspeed = 3;

const int led = 7;

Rx16Response zbRx = Rx16Response();

XBee xbee = XBee();

void setup() {

Serial.begin(9600); // opens serial port, sets data rate to 9600 bps
xbee.setSerial(Serial);

pinMode(led, OUTPUT);

pinMode(dirA, OUTPUT);
pinMode(brake, OUTPUT);
pinMode(mspeed, OUTPUT);

// blink twice at startup
digitalWrite(led, LOW);
delay(1000);

digitalWrite(led, HIGH); // first blink
delay(50);
digitalWrite(led, LOW);
delay(200);
digitalWrite(led, HIGH); // second blink
delay(50);
digitalWrite(led, LOW);
}

void loop() {
digitalWrite(dirA, LOW);
digitalWrite(brake, LOW);

// send data only when you receive data:
if (Serial.available() > 0) {
// read the incoming byte:
incomingByte = Serial.read();

if(incomingByte == ‘0’){
digitalWrite(led, LOW);
analogWrite(mspeed, 0);
}
else if(incomingByte == ‘1’){
digitalWrite(led, HIGH);
analogWrite(mspeed, 255);
}

}

}

[/c]

To-Dos:

  1. add speed control! More speed! Much more!
  2. … ideas …?

php preg_replace problem with large files/strings – fails silently

Martin Zellerphp Leave a Comment

If you have a problem with preg_replace or other PCRE functions in php, because they fail  silently, without error message or other feedback, especially with large strings, then have a look at this php.ini setting:

pcre.backtrack_limit

For example, modify the value in your php script:

[php] ini_set(‘pcre.backtrack_limit’, ‘100000000’);
[/php]

Just increase the default value of 1.000.000. Maybe you have to increase the value for memory_limit as well.

zf2 – translatePlural example with Poedit

Martin Zellerphp, zf2 1 Comment

I’d like to explain how to use the translatePlural method along with the Zend Framework 2.
(I’m assuming that you have been configured Poedit and the Zend Framework so that it works with the normal translate method)

The use of translatePlural is actually quite simple. Let us first look at the normal translate method:

[php] echo $this->translate(‘example.key’);
[/php]

After parsing your code files with Poedit, you add your text for the key “example.key”. For example: “My sample text”.

Poedit adds the following lines to the appropriate *.po file:

[plain] msgid "example.key"
msgid "My sample text"
[/plain]

Now to translatePlural. An example:

NOTE: The sample is suitable for languages with one plural form as English or German. For languages with complex plural forms, for example, Russian, see the note at the bottom!

Suppose you want to echo the text “tooth” if $count is equal to 1 and “teeth” if $count is greater than 1:

[php] $count = rand(1,32); // Generates a random integer between 1 and 32 (inclusive)
echo $this->translatePlural(‘example.key.singular’, ‘example.key.plural’, $count);
[/php]

Simple. The first parameter is the text key for the text if $count is equal to 1 and the second parameter is for $count greater than 1.

What does NOT work:
If you add separate entries for the singular and the plural keys to Poedit, you’ll achieve nothing.

What you need to do:
You must configure Poedit so that it finds “translatePlural” in our project files and interprets it correctly!

Open Poedit and go to: Catalog > Properties
In the first tab you find an input option for “plural forms”.
Enter:

[plain] nplurals=2; plural=(n != 1);
[/plain]

So you say Poedit that we want to use a language with one plural form, in which everything that is not 1, should be plural. So exactly what we need for our example:
1 tooth, 2 teeth, 3 teeth …

Thus Poedit scans your code files right for “translatePlural” you have to add a new keyword in the third tab:
(The keyword “translate” you probably already added)

[plain] translatePlural:1,2
[/plain]

If you then do the update with Poedit, you’ll find input options like this:

poedit-translateplural

This is the result in the appropriate *.po file:

[plain] msgid "example.key.singular"
msgid_plural "example.key.plural"
msgstr[0] "tooth"
msgstr[1] "teeth"
[/plain]

Note for languages with complex plural forms:
Our example is suitable for languages, which has one plural form. The syntax for other languages can be found here:
Overview of plural form syntax

Hint: by default the Zend Framework works only with one plural form. For more complex plural forms you have to work with the optional TextDomain parameter of the method translatePlural. To understand why, have a look at some code of the TextDomain instance that is used by default:

[php] /**
* Returns a shared default plural rule.
*
* @return PluralRule
*/
public static function getDefaultPluralRule()
{
if (static::$defaultPluralRule === null) {
static::$defaultPluralRule = PluralRule::fromString(‘nplurals=2; plural=n != 1;’);
}

return static::$defaultPluralRule;
}
[/php]

Subdomain based mobile versions in zf2 projects

Martin Zellerphp, zf2 Leave a Comment

A few weeks ago it was my job to extend an existing Zend Framework 2 project with a mobile version. The orderer wanted when calling mobile.example.com/path1 a for mobile clients optimized version of www.example.com/path1 should be delivered. Some mobile versions of pages should have only a changed layout, while others should provide additional content.

These requirements (for requests with the “mobile” subdomain) I wanted to implement like this:

  1. an additional layout.phtml for the mobile layout
  2. check if there is a view phtml in a “mobile” folder => if yes: use this view script (with the same controller) – if no: use the default view script
  3. check if there is a controller in a “mobile” folder => if yes: use this controller – if no: use the default controller

An example folder structure for the “Application” module:

[plain] Application
|- src
| |-Application
| | |- Controller
| | | |- Mobile
| | | | |- IndexController.php
| | | |- IndexController.php
| | | |- TestController.php
|- view
| |- application
| | |- index
| | | |- index.phtml
| | | |- indexaction2.phtml
| | |- test
| | | |- test.phtml
| |- layout
| | |- layout.phtml
| |- mobile
| | |- application
| | | |- index
| | | | |- indexaction2.phtml
| | | |- test
| | | | |- test.phtml
| | |- layout
| | | |- layout.phtml
[/plain]

For our example let’s assume this route definition: /app/:controller/:action

Now let’s have a look to some requests:

  • http://www.example.com/app/index/index => would use Application\Controller\IndexController.php and application\index\index.phtml
  • http://mobile.example.com/app/index/index => would use Application\Controller\Mobile\IndexController.php and fall back to application\index\index.phtml (because we don’t have a mobile\application\index\index.phtml)
  • http://www.example.com/app/test/test => would use Application\Controller\TestController.php and application\test\test.phtml
  • http://mobile.example.com/app/test/test => would fall back to Application\Controller\TestController.php (because we don’t have a Application\Controller\Mobile\TestController.php) and use mobile\application\test\test.phtml
  • http://mobile.example.com/app/index/indexaction2 => would use both mobile versions: Application\Controller\Mobile\IndexController.php and mobile\application\index\indexaction2.phtml
  • http://mobile.example.com/* => all requests via “mobile” subdomain would use the layout of mobile/layout/layout.phtml

First, we take care of the handling of the layout. For this, we define another default layout plugin called “SubDomainBasedLayoutControllerPlugin”. This class we let inherit from the ZF2 standard layout plugin and override the method “setTemplate”:

[php] namespace Application\Controller\Plugin;

use Zend\Mvc\Controller\Plugin\Layout;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorAwareTrait;

class SubDomainBasedLayoutControllerPlugin extends Layout implements ServiceLocatorAwareInterface {

use ServiceLocatorAwareTrait;

/**
* Set the layout template
*
* @param string $template
* @return Layout
*/
public function setTemplate($template)
{
$viewModel = $this->getViewModel();

/** @var \Zend\Uri\Http $uri */
$uri = $this->getServiceLocator()->getServiceLocator()->get(‘Request’)->getUri();
$host = $uri->getHost();

$prts = explode(‘.’, $host);
$subDomain = array_shift($prts);

if(‘mobile’==$subDomain)
$template = ‘mobile/’ . $template;

$viewModel->setTemplate((string) $template);
return $this;
}

}
[/php]

The definition for our module.config.php:

[php] ‘controller_plugins’ => array(
‘invokables’ => array(
‘layout’ => ‘Application\Controller\Plugin\SubDomainBasedLayoutControllerPlugin’,
)
),
[/php]

For the handling of the optional mobile controllers we override the ZF2 standard ControllerLoaderFactory and the ZF2 standard ControllerManager:

[php] namespace Application\Service\Factory;

use Application\Controller\Manager\SubDomainBasedControllerManager;
use Zend\Mvc\Service\ControllerLoaderFactory;
use Zend\ServiceManager\ServiceLocatorInterface;

class SubDomainBasedControllerLoaderFactory extends ControllerLoaderFactory {

public function createService(ServiceLocatorInterface $serviceLocator)
{
$controllerLoader = new SubDomainBasedControllerManager();
$controllerLoader->setServiceLocator($serviceLocator);
$controllerLoader->addPeeringServiceManager($serviceLocator);

$config = $serviceLocator->get(‘Config’);

if (isset($config[‘di’]) && isset($config[‘di’][‘allowed_controllers’]) && $serviceLocator->has(‘Di’)) {
$controllerLoader->addAbstractFactory($serviceLocator->get(‘DiStrictAbstractServiceFactory’));
}

return $controllerLoader;
}

}
[/php]

Our SubDomainBasedControllerLoaderFactory uses our SubDomainBasedControllerManager. The interesting part here is that our ControllerManager searches for a controller with the suffix “_Mobile”:

[php] namespace Application\Controller\Manager;

use Zend\Mvc\Controller\ControllerManager;
use Zend\Console\Request as ConsoleRequest;

class SubDomainBasedControllerManager extends ControllerManager {

/**
* Override: do not use peering service managers
*
* @param string $name
* @param array $options
* @param bool $usePeeringServiceManagers
* @return mixed
*/
public function get($name, $options = array(), $usePeeringServiceManagers = false)
{
/** @var \Zend\Stdlib\RequestInterface $request */
$request = $this->getServiceLocator()->get(‘Request’);

if ($request instanceof ConsoleRequest)
return parent::get($name, $options, $usePeeringServiceManagers);

/** @var \Zend\Uri\Http $uri */
$uri = $this->getServiceLocator()->get(‘Request’)->getUri();
$host = $uri->getHost();

$prts = explode(‘.’, $host);
$subDomain = array_shift($prts);

if($subDomain==’mobile’) {

$newName = $name . ‘_Mobile’;

if($this->has($newName, true, false))
$name = $newName;

}

return parent::get($name, $options, $usePeeringServiceManagers);
}

}
[/php]

The definition in our module.config.php:

[php] ‘service_manager’ => array(
‘factories’ => array(
‘ViewTemplatePathStack’ => ‘Application\Service\Factory\SubDomainBasedTemplatePathStackFactory’,
‘ViewTemplateMapResolver’ => ‘Application\Service\Factory\SubDomainBasedViewTemplateMapResolverFactory’,
‘ControllerLoader’ => ‘Application\Service\Factory\SubDomainBasedControllerLoaderFactory’,
),
),
[/php]

Attention: If you look at these definitions above, then you realize that we’ve already defined the factories for our mobile view handling: ViewTemplatePathStack and ViewTemplateMapResolver
There are two factories, because we also have to consider any template maps (ViewTemplateMapResolver)!

First the code for the ViewTemplatePathStack factory:

[php] namespace Application\Service\Factory;

use Application\View\Resolver\SubDomainBasedTemplatePathStack;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;

class SubDomainBasedTemplatePathStackFactory implements FactoryInterface {

/**
* Create service
*
* @param ServiceLocatorInterface $serviceLocator
* @return mixed
*/
public function createService(ServiceLocatorInterface $serviceLocator)
{
$config = $serviceLocator->get(‘Config’);

$templatePathStack = new SubDomainBasedTemplatePathStack();

$templatePathStack->setServiceLocator($serviceLocator);

if (is_array($config) && isset($config[‘view_manager’])) {
$config = $config[‘view_manager’];
if (is_array($config)) {
if (isset($config[‘template_path_stack’])) {
$templatePathStack->addPaths($config[‘template_path_stack’]);
}
if (isset($config[‘default_template_suffix’])) {
$templatePathStack->setDefaultSuffix($config[‘default_template_suffix’]);
}
}
}

return $templatePathStack;
}

}
[/php]

In the factory above we create the service SubDomainBasedTemplatePathStack:

[php] namespace Application\View\Resolver;

use SplFileInfo;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorAwareTrait;
use Zend\View\Exception;
use Zend\View\Exception\DomainException;
use Zend\View\Renderer\RendererInterface as Renderer;
use Zend\View\Resolver\TemplatePathStack;

class SubDomainBasedTemplatePathStack extends TemplatePathStack implements ServiceLocatorAwareInterface {

use ServiceLocatorAwareTrait;

/**
* Retrieve the filesystem path to a view script
*
* @param string $name
* @param null|Renderer $renderer
* @return string
* @throws \Zend\View\Exception\DomainException
*/
public function resolve($name, Renderer $renderer = null)
{
$this->lastLookupFailure = false;

if ($this->isLfiProtectionOn() && preg_match(‘#\.\.[\\\/]#’, $name)) {
throw new DomainException(
‘Requested scripts may not include parent directory traversal ("../", "..\\" notation)’
);
}

if (!count($this->paths)) {
$this->lastLookupFailure = static::FAILURE_NO_PATHS;
return false;
}

// Ensure we have the expected file extension
$defaultSuffix = $this->getDefaultSuffix();
if (pathinfo($name, PATHINFO_EXTENSION) == ”) {
$name .= ‘.’ . $defaultSuffix;
}

/** @var \Zend\Uri\Http $uri */
$uri = $this->getServiceLocator()->get(‘Request’)->getUri();
$host = $uri->getHost();

$prts = explode(‘.’, $host);
$subDomain = array_shift($prts);

foreach ($this->paths as $path) {

// mzeller: THIS IS THE NEW MOBILE CHECK PART
if($subDomain==’mobile’) {
// check mobile folder first
$file = new SplFileInfo($path . ‘mobile’ . DIRECTORY_SEPARATOR . $name);

($path . $appKey . DIRECTORY_SEPARATOR . $name);

if ($file->isReadable()) {
// Found! Return it.
if (($filePath = $file->getRealPath()) === false && substr($path, 0, 7) === ‘phar://’) {
// Do not try to expand phar paths (realpath + phars == fail)
$filePath = $path . ‘mobile’ . DIRECTORY_SEPARATOR . $name;
if (!file_exists($filePath)) {
break;
}
}
if ($this->useStreamWrapper()) {
// If using a stream wrapper, prepend the spec to the path
$filePath = ‘zend.view://’ . $filePath;
}
return $filePath;
}
}

$file = new SplFileInfo($path . $name);

($path . $name);

if ($file->isReadable()) {
// Found! Return it.
if (($filePath = $file->getRealPath()) === false && substr($path, 0, 7) === ‘phar://’) {
// Do not try to expand phar paths (realpath + phars == fail)
$filePath = $path . $name;
if (!file_exists($filePath)) {
break;
}
}
if ($this->useStreamWrapper()) {
// If using a stream wrapper, prepend the spec to the path
$filePath = ‘zend.view://’ . $filePath;
}
return $filePath;
}
}

$this->lastLookupFailure = static::FAILURE_NOT_FOUND;
return false;
}

}
[/php]

Here I had to copy the code from the parent class and to extend with new code. Search for “THIS IS THE NEW MOBILE CHECK PART”.

The other factory was for the ViewTemplateMapResolver:

[php] namespace Application\Service\Factory;

use Application\View\Resolver\SubDomainBasedTemplateMapResolver;
use Zend\Mvc\Service\ViewTemplateMapResolverFactory;
use Zend\ServiceManager\ServiceLocatorInterface;

class SubDomainBasedViewTemplateMapResolverFactory extends ViewTemplateMapResolverFactory {

/**
* Create the template map view resolver
*
* Creates a Zend\View\Resolver\AggregateResolver and populates it with the
* [‘view_manager’][‘template_map’] *
* @param ServiceLocatorInterface $serviceLocator
* @return ClientBasedTemplateMapResolver
*/
public function createService(ServiceLocatorInterface $serviceLocator)
{
$config = $serviceLocator->get(‘Config’);
$map = array();
if (is_array($config) && isset($config[‘view_manager’])) {
$config = $config[‘view_manager’];
if (is_array($config) && isset($config[‘template_map’])) {
$map = $config[‘template_map’];
}
}
return new SubDomainBasedTemplateMapResolver($map, $serviceLocator);
}

}
[/php]

In this factory above we create the service SubDomainBasedTemplateMapResolver:

[php] namespace Application\View\Resolver;

use Zend\Console\Request as ConsoleRequest;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorAwareTrait;
use Zend\View\Resolver\TemplateMapResolver;

class SubDomainBasedTemplateMapResolver extends TemplateMapResolver implements ServiceLocatorAwareInterface {

use ServiceLocatorAwareTrait;

/**
* Retrieve a template path by name
*
* @param string $name
* @return false|string
* @throws \Zend\View\Exception\DomainException if no entry exists
*/
public function get($name)
{
if($this->getServiceLocator()->get(‘Request’) instanceof ConsoleRequest) {
return parent::get($name);
}

/** @var \Zend\Uri\Http $uri */
$uri = $this->getServiceLocator()->get(‘Request’)->getUri();
$host = $uri->getHost();

$prts = explode(‘.’, $host);
$subDomain = array_shift($prts);

$subDomainTemplate = null;
if($subDomain==’mobile’)
$subDomainTemplate = ‘mobile/’ . $name;

if($subDomainTemplate!=null && $this->has($subDomainTemplate))
return $this->map[$subDomainTemplate];

if (!$this->has($name)) {
return false;
}
return $this->map[$name];
}

}
[/php]

How to define the mobile IndexController from our example above?

[php] ‘controllers’ => array(
‘invokables’ => array(
‘Application\Controller\Index’ => ‘Application\Controller\IndexController’,
‘Application\Controller\Test’ => ‘Application\Controller\TestController’,

‘Application\Controller\Index_Mobile’ => ‘Application\Controller\Mobile\IndexController’,
)
),
[/php]

In conclusion, an example of how our mobile IndexController might look like:

[php] namespace Application\Controller\Mobile;

use Application\Controller\IndexController as BaseIndexController;
use Zend\View\Model\ViewModel;

class IndexController extends BaseIndexController {

public function indexAction()
{
$parentViewModel = parent::indexAction();
$parentViewModel->setVariable(‘specialMobileValue’, ‘yeah’);
return $parentViewModel;
}

}
[/php]

zf2 – logger problems with the FirePhp/FireBug writer in a console request

Martin Zellerphp, zf2 Leave a Comment

In my  recent zf2 (zend framework 2.1) project module I defined a logger with a stream writer and a firephp writer. I am using the same module also for some console requests (Zend\Console). The problem I had was that the system complains of the firephp logger when I did a console request:

[plain]

======================================================================
The application has thrown an exception!
======================================================================
Exception
Headers already sent in C:\…\Zend\Console\Adapter\AbstractAdapter.php
on line 56. Cannot send log data to FirePHP. You must have Output Buffering
enabled via ob_start() or output_buffering ini directive.
———————————————————————-

[/plain]

Short: Cannot send log data to FirePHP. 

So I changed my local.php file to differentiate between a HTTP request and a console request.

This is the important part of the service_manager/factories configuration:

[php]

‘Zend\Log\Logger’ => function($sm) {
$request = $sm->get(‘Request’);
$logger = new \Zend\Log\Logger();
if ($request instanceof Zend\Console\Request) {
$file = new \Zend\Log\Writer\Stream(__DIR__ . ‘/../../data/dev-console.log’);
$logger->addWriter($file);
} else {
$writer_firebug = new \Zend\Log\Writer\FirePhp();
$writer_stream = new \Zend\Log\Writer\Stream(__DIR__ . ‘/../../data/dev.log’);
$logger->addWriter($writer_firebug);
$logger->addWriter($writer_stream);
}

return $logger;
}

[/php]

zf2 – Get the route name in a custom view helper

Martin Zellerphp, zf2 4 Comments

========================
IMPORTANT UPDATE: this is a very old post – find a better way in my post “zf2/zf3 – get the route name in a custom view helper
========================

 

Ever tried to get the route name in your custom view helper in a zf2 (zend framework 2.1) project?
I searched a lot – until I realized that I have to call getServiceLocator on my serviceLocator instance 😉
This is the solution:

<?php
namespace YouCompany\YourNameSpace;

use Zend\ServiceManager\ServiceLocatorInterface;

use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\View\Helper\AbstractHelper;

class YourViewHelper extends AbstractHelper implements ServiceLocatorAwareInterface {

private $serviceLocator;

 public function __invoke() {
   $routeMatch = $this->serviceLocator->getServiceLocator()->get('Application')->getMvcEvent()->getRouteMatch();
   echo $routeMatch->getMatchedRouteName();
   die;
 }

 /**
 * Set service locator
 *
 * @param ServiceLocatorInterface $serviceLocator
 */
 public function setServiceLocator(ServiceLocatorInterface $serviceLocator) {
   $this->serviceLocator = $serviceLocator;
 }

 /**
 * Get service locator
 *
 * @return ServiceLocatorInterface
 */
 public function getServiceLocator() {
   return $this->serviceLocator;
 }
}

zf2, doctrine module and expressions (like ‘IS NOT NULL’)

Martin Zellerdoctrine, php, zf2 Leave a Comment

Currently I am working on a migration of a zend framework 1 project to zf2 (zend framework 2). Furthermore I want to introduce Doctrine to the project. There are some useful articles about installing and using the zf2 doctrine module. After some hours I found out how to use modules and Doctrine in zf2.
Sometimes you will have code like this:

[php] // em: \Doctrine\ORM\EntityManager
$result = $this->em->getRepository(‘\YourNameSpace\Model\ModelObject’)->findBy(array(‘column1’=>’xyz’), array(‘orderBy’=>’asc’));
//[/php]

You can find all this stuff with google, BUT what is hard to find out: how to use expressions (like ‘IS NOT NULL’) with Doctrine with the zf2 doctrine module.
What if you want to add to our previous statement “and column2 is not null”?
See the solution:

[php] // em: \Doctrine\ORM\EntityManager
$qb = $this->em->getRepository(‘\YourNameSpace\Model\ModelObject’)->createQueryBuilder(‘c’);
// add first where clause
$qb->where($qb->expr()->eq(‘c.column1′,’:ccol1′));
// add the second where clause with our "IS NOT NULL" expression
$qb->andWhere($qb->expr()->isNotNull(‘c.column2’));
// set the parameters
$qb->setParameter(‘ccol1’, ‘xyz’);
// get the query
$query = $qb->getQuery();
// fetch the result
$result = $query->getResult();
//
[/php]

For the other expressions look into the class Doctrine\ORM\Query\Expr .

Automatic resizing of wickets ModalWindow

Martin Zellerjava, wicket 5 Comments

Today I ran into problems with wickets ModalWindow. I have got a modal dialog with a list and a form in it. The user can add a row to this list with the form. Problem was that the size of the modal window stayed as it was although the list was growing and growing. Nasty scroll bars appeared around my modal dialog. So I’d like to make the dialog to “auto resize” on each post.

My solution was simple. I just added this line of code in the onsubmit handler of the ajax button:

[java] // in the onsubmit handler of the ajax button
target.appendJavaScript("window.parent.Wicket.Window.current.autoSizeWindow();");
//[/java]

It’s like magic. Now the modal dialog always has the proper size 😉
(Tested with wicket version 1.5.5)

EDIT1: also have a look at Williams note below (the first comment)

EDIT2: also consider Nikkes solution!