Multiple class inheritance with Doctrine ODM in Symfony: One collection for multiple document types

When you use Doctrine ORM in Symfony all the data is stored in tables having every row the same columns. Storing different type of entities in the same table is not posible (AFAIK). But MongoDB can store any kind of document in the same collection making possible to have different kind of objects in the same place and thus making inheritance easy to use if you need it.

doctrine-odm-mongodb

In this example I’m going to create the documents needed to reflect an RFID system (from a real application I did sometime ago) where cards, called internally tag, can represent different things: a person, an item, an event, etc. Thus our mongodb database will have a collection of tags with documents of type person, item, event, etc.

Each time a card is used the system will know what it represents using the internal EPC of it: as you may be thinking this will be our ID.

Install MongoDB, the PHP driver and create a new database

This process depends on the operating system you use. Here are the instructions for some of them in older posts:

To create a new database in our mongodb server you have just to make a use statement and create a new collection in it with the mongo client:

> use app
switched to db app
> db.createCollection('tags')
{ "ok" : 1 }

Install MongoDB Doctrine library and DoctrineMongoDBBundle

This process is needed to be able to use MongoDB within the Symfony project. Those steps are extracted from the Symfony DoctrineMongoDBBundle Documentation.

Add the MongoDB Doctrine library and the DoctrineMongoDBBundle to composer.json:

{
    "require": {
        "doctrine/mongodb-odm": "~1.0",
        "doctrine/mongodb-odm-bundle": "~3.0"
    },
}

Now install them using composer. Using a local installation of it:

$ php composer.phar update doctrine/mongodb-odm doctrine/mongodb-odm-bundle

Or a global installation:

$ composer update doctrine/mongodb-odm doctrine/mongodb-odm-bundle

Register the annotations library by adding the following to the autoloader (below the existing AnnotationRegistry::registerLoader line):

use Doctrine\ODM\MongoDB\Mapping\Driver\AnnotationDriver;
AnnotationDriver::registerAnnotationClasses();

It should look something like this:

<?php
// app/autoload.php
use Doctrine\Common\Annotations\AnnotationRegistry;
use Doctrine\ODM\MongoDB\Mapping\Driver\AnnotationDriver;
use Composer\Autoload\ClassLoader;

/**
 * @var ClassLoader $loader
 */
$loader = require __DIR__.'/../vendor/autoload.php';

AnnotationRegistry::registerLoader(array($loader, 'loadClass'));
AnnotationDriver::registerAnnotationClasses();

return $loader;

Now update AppKernel.php:

// app/AppKernel.php
public function registerBundles()
{
    $bundles = array(
        // ...
        new Doctrine\Bundle\MongoDBBundle\DoctrineMongoDBBundle(),
    );

    // ...
}

Configure MongoDB ODM

Symfony must know how to connect to our mongodb instance. This is configured in app/config/config.yml being the easiest way using the auto_mapping option:

# app/config/config.yml
doctrine_mongodb:
    connections:
        default:
            server: mongodb://localhost:27017
            options: {}
    default_database: test_database
    document_managers:
        default:
            auto_mapping: true

I like using the app/config/parameters.yml to set up the host, port and database name:

# app/config/parameters.yml
parameters:
    mongodb_host: 127.0.0.1
    mongodb_port: 27017
    mongodb_name: app
# app/config/config.yml
imports:
    - { resource: parameters.yml }

doctrine_mongodb:
    connections:
        default:
            server: mongodb://%mongodb_host%:%mongodb_port%
            options: {}
    default_database: %mongodb_name%
    document_managers:
        default:
            auto_mapping: true

The document classes

Now is when we start coding. I’ll suppose you already have an AppBundle created (if not look here at the Symfony Best Practices – Creating the project).

The “parent” class: Single Collection Inheritance

All the different things we want to represent as a document in mongodb in the same collection and an object in PHP must inherit from the same class called tag. The “magic” here is the @MongoDB\InheritanceType("SINGLE_COLLECTION") annotation. This will tell MongoDB ODM that this collection will store different documents and a discrimination field will tell each other apart.

<?php
// src/AppBundle/Document/TagInterface.php

namespace AppBundle\Document;

interface TagInterface
{
}
<?php
// src/AppBundle/Document/Tag.php

namespace AppBundle\Document;

use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;

/**
 * @MongoDB\Document(collection="tags")
 * @MongoDB\InheritanceType("SINGLE_COLLECTION")
 * @MongoDB\DiscriminatorField("type")
 * @MongoDB\DiscriminatorMap({"person"="Person", "item"="Item"})
 */
class Tag implements TagInterface
{
    /**
     * @MongoDB\Id(strategy="NONE")
     */
    protected $epc;

    /**
     * Set epc
     *
     * @param custom_id $epc
     * @return self
     */
    public function setEpc($epc)
    {
        $this->epc = $epc;
    }

    /**
     * Get epc
     *
     * @return custom_id $epc
     */
    public function getEpc()
    {
        return $this->epc;
    }
}

The inherited classes: extends

Now that we have the parent class the only thing we need to store documents in that collection is extend it:

<?php
// src/AppBundle/Document/Person.php

namespace AppBundle\Document;

use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;

/**
 * @MongoDB\Document
 */
class Person extends Tag
{
    /**
     * @MongoDB\String
     */
    protected $name;

    /**
     * Set name
     *
     * @param string $name
     * @return self
     */
    public function setName($name)
    {
        $this->name = $name;

        return $this;
    }

    /**
     * Get name
     *
     * @return string $name
     */
    public function getName()
    {
        return $this->name;
    }
}
<?php
// src/AppBundle/Document/Item.php

namespace AppBundle\Document;

use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;

/**
 * @MongoDB\Document
 */
class Item extends Tag
{
    /**
     * @MongoDB\Float
     */
    protected $price;

    /**
     * Set price
     *
     * @param float $price
     * @return self
     */
    public function setPrice($price)
    {
        $this->price = $price;

        return $this;
    }

    /**
     * Get price
     *
     * @return float $price
     */
    public function getPrice()
    {
        return $this->price;
    }
}

Store and read documents to/from the collection

All the configuration and classes are done, we can start persisting and finding documents anywhere in the project: a controller, a command, etc. For example lets persist and find some Person and Item objects in a controller:

<?php
// src/AppBundle/Controller/TagsController.php

namespace AppBundle\Controller;

use AppBundle\Document\Item;
use AppBundle\Document\Person;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

class TagsController extends Controller
{
    /**
     * @Route("/persist", name="app_persist")
     * @param Request $request The Request object
     * @return Response A Response instance
     */
    public function persistAction()
    {
        $item = new Item();
        $item->setEpc('0000000000000000000000AA');
        $item->setPrice('19.99');

        $person = new Person();
        $item->setEpc('0000000000000000000000AB');
        $item->setName('Eneko');

        $dm = $this->get('doctrine_mongodb')->getManager();
        $dm->persist($item);
        $dm->persist($person);
        $dm->flush();

        return new Response('Created tags');
    }

    /**
     * @Route("/find", name="app_find")
     * @param Request $request The Request object
     * @return Response A Response instance
     */
    public function findAction(Request $request)
    {
        $epc = $request->request->get('epc');
        $tag = $this->get('doctrine_mongodb')
            ->getRepository('AppBundle:Tag')
            ->find($epc);

        if (!$tag) {
            throw $this->createNotFoundException('No tag found for epc '.$epc);
        }

        switch(true) {
            case $tag instanceof Person:
                $type = 'Person';
                break;
            case $tag instanceof Item:
                $type = 'Item';
                break;
        }

        return new Response('Found '.$type);
    }

    /**
     * @Route(
     *     "/find-with-param-converter/{epc}",
     *     requirements={
     *         "epc": "[a-zA-Z0-9]{24}"
     *     },
     *     name="app_find_with_param_converter"
     * )
     * @ParamConverter("tag", class="AppBundle\Document\Tag")
     * @param Request $request The Request object
     * @param TagInterface $tag A object of class Tag or one of its children
     * @return Response A Response instance
     */
    public function findWithParamConverterAction(Request $request, TagInterface $tag)
    {
        switch(true) {
            case $tag instanceof Person:
                $type = 'Person';
                break;
            case $tag instanceof Item:
                $type = 'Item';
                break;
        }

        return new Response('Found '.$type);
    }
}

And how are all those object stored in mongodb and how can Doctrine know what class each one represents? That’s configured in the parent class with the @MongoDB\DiscriminatorField and the @MongoDB\DiscriminatorMap annotations. Each document simply has a field named type with the name of the class on it:

> db.tags.find()
{
    "_id" : "0000000000000000000000AA",
    "type" : "item",
    "price" : 19.99
},
{
    "_id" : "0000000000000000000000AB",
    "type" : "person",
    "name" : "Eneko"
}

Ref: http://doctrine-mongodb-odm.readthedocs.org/en/latest/reference/basic-mapping.html
http://doctrine-mongodb-odm.readthedocs.org/en/latest/reference/inheritance-mapping.html
http://jwage.com/post/30490180105/inheritance-and-mapped-super-classes-in-doctrine

Install MongoDB and the PHP driver in CentOS

Install MongoDB server

Create the /etc/yum.repos.d/mongodb-org-3.0.repo file with this content:

[mongodb-org-3.0]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/3.0/x86_64/
gpgcheck=0
enabled=1

Then install mongodb with this commando:

# yum install -y mongodb-org

This are the important files and folders for MongoDB in CentOS:

  • Configuration file: /etc/mongod.conf
  • Data files: /var/lib/mongo/
  • Logs files: /var/log/mongodb/

By default the used port is 27017 and also you can ONLY make connections to it within the localhost or 127.0.0.1. If you want to change that you have to edit the bind_ip option:

bind_ip=127.0.0.1

To start the server run this command:

# service mongod start

To stop the server run this command:

# service mongod stop

If you want the server to start on boot:

# chkconfig mongod on

Once the service is running we can connect to it with the mongo command:

$ mongo
MongoDB shell version: 3.0.6
connecting to: test
Welcome to the MongoDB shell.
For interactive help, type "help".
For more comprehensive documentation, see
	http://docs.mongodb.org/
Questions? Try the support group
	http://groups.google.com/group/mongodb-user
>

Install the PHP driver

Install the required dependencies and the mongo PHP driver itself:

# yum -y install gcc openssl-devel php-devel php-cli php-pear
# pecl install mongo

Note: the library will be installed in /usr/lib64/php/modules/mongo.so

Activate it in /etc/php.ini or in it’s own file in /etc/php.d/30-mongo.ini:

extension=mongo.so

And finally restart httpd:

# service httpd restart

mongodb-logo

Ref: http://docs.mongodb.org/manual/tutorial/install-mongodb-on-red-hat/
http://docs.mongodb.org/ecosystem/drivers/php/

Install Android SDK and Tools with Eclipse in Ubuntu, Mac OS X and Windows

Here I’ll explain the process to install Android SDK and Tools. There are 4 things you have to install in order to develop Android applications:

  • Android SDK
  • Eclipse
  • Android Development Tools plugins for Eclipse (ADT)
  • Android Virtual Device (ADV)

Android SDK

You can download the latest version of the Android SDK for your OS at the official Android SDK web. Latest versions at the time of this writing are those:

Ubuntu android-sdk_r24.4.1-linux.tgz
Mac OS X android-sdk_r24.4.1-macosx.zip
Windows android-sdk_r24.4.1-windows.zip

Once downloaded uncompress it anywhere you like in your hard drive. It can be for example in your users home folder (/home/username/android-sdk-linux for Ubuntu, /Users/username/Applications/android-sdk-macosx for Mac OS X, C:\Documents and Settings\username\android-sdk-windows for Windows XP, C:\Users\username\android-sdk-windows for Windows Vista/7/8/10) or beside other applications (/usr/lib/android-sdk-linux for Ubuntu, /Applications/android-sdk-macosx for Mac OS X, C:\Program Files\android-sdk-windows for Windows).

Be sure you have configured the JAVA_HOME, ANDROID_HOME and PATH environment variables:

In Linux edit the ~/.bashrc file:

export JAVA_HOME=$(readlink -f /usr/bin/java | sed "s:/bin/javac::")
export ANDROID_HOME=~/android-sdk-linux
export PATH=$PATH:~/android-sdk-linux/tools

For Mac OS X you should edit the ~/.bash_profile file:

export JAVA_HOME=$(/usr/libexec/java_home)
export ANDROID_HOME=~/Applications/android-sdk-macosx
export PATH=$PATH:~/Applications/android-sdk-macosx/tools

Eclipse

For Eclipse installation just go to https://eclipse.org/downloads/, download it and install it.

Android Development Tools

Open Eclipse and go to “Help→Install New Software”, click “Add…” and enter “ADT Plugin” for “Name” and “https://dl-ssl.google.com/android/eclipse/” for “Location”.

eclipse-add-new-software

eclipse-add-new-software-adt-plugin

Select that repository in “Work with:”, choose all the packages and proceed the installation. Click OK if you see a message saying that unsigned content is being installed . Once installed you have to restart Eclipse.

After the restart, Eclipse will ask you to configure the Android SDK location. Click “Open Preferences”, select “Use existing SDKs” and fill “Existing Location” with the path you used to decompress Android SDK. If you don’t configure it now you can configure it anytime in the Eclipse preferences under the Android options.

eclipse-preferences-android

After configuring the Android SDK location path you will be asked to install SDK Platform Tools. Click “Open SDK Manager” (or select “Window->Android SDK Manager”), select all the packages inside “Tools” and the version of Android of your choice (I used 4.2) and click “Install X packages…”. Don’t worry if you see “Stopping ADB server failed (code -1).” in the log messages, it’s OK.

Android Virtual Device

Now that everything is installed you have to create an Android Virtual Device (AVD) so you can use the emulator. Open the Java perspective (Window->Open Perspective->Java or click the icon with the yellow plus sign at the top right of the IDE and select Java). You will see 2 new icons with the Android symbol in the tools bar. Click the one on the right that looks like a phone (the other one opens the Android SDK Manager), click “New…” and give your Android Virtual Device a name, a device type, a target, etc. If you can’t select a target just restart Eclipse so it can load the newly installed SDK Platform Tools.

In Ubuntu for 64 bit systems you can get this error when creating an AVD:

Error: Failed to create the SD card.
Error: Failed to create sdcard in the AVD folder.

To solve it install the ia32-libs libraries (http://stackoverflow.com/questions/3878445/ubuntu-error-failed-to-create-the-sd-card). Beware that they are about 260MB:

sudo apt-get install ia32-libs

Your first Android Application

Now you are ready to create your first Android application. Create a new “Android Application Project” with all the default options, just fill a “Application Name” like “Hello World”. The default option uses a “BlankActivity” with a template that needs the the Adroid Support library. It will ask you to install it and is as simple as clicking the “Install/Upgrade” button showed in that dialog window.

And there you have a fresh new “Hello Word” Android application. If you make the mistake of clicking the “Run” button you will get an error like this:

Error in an XML file: aborting build

That’s because by default the opened file after creating this “Hello World” project is “activity_main.xml”. You can also notice that a new file called “activity_main.out.xml” has been created, and it even is marked with errors. Delete that newly created file and open the “MainActivity.java” file under “src” folder. Now you can click the “Run” button. Select “Android Application” in the “Run As” dialog window.

I had a problem in Mac OS X Lion after running the emulator several times. When I tried to run the application the emulator crashed every time. Searching on the Internet I found this page where it explains that if you run the emulator in a secondary monitor and close it while in that monitor, a value of the X position of the window is stored and next time that value is bigger that the first monitors resolution and chashes. Deleting the emulator-user.ini file from the /Users/username/.android/avd/device_name folder (executing open /Users/username/.android/avd/device_name from a terminal window) and running the emulator again solved the problem.

As a hint I always deselect “Project->Build Automatically” option because I prefer to build the code manually pressing Control+B or Command+B.

Sources: http://developer.android.com/sdk/installing/installing-adt.html
http://developer.android.com/sdk/installing/adding-packages.html

Remove a WordPress action in child theme

If you want to remove a WordPress action that was added in the parent theme like this:

function fusion_insert_og_meta() {
    ...
}
add_action( 'wp_head', 'fusion_insert_og_meta', 5 );

It’s VERY important to use the same priority value in the remove action in case a different value from the default (10) was used:

function remove_parent_actions() {
    remove_action( 'wp_head', 'fusion_insert_og_meta', 5 );
}
add_action( 'init', 'remove_parent_actions' );

wordpress

Ref: http://themeshaper.com/2009/05/25/action-hooks-wordpress-child-themes/

Adding PHP 7.0 support in Plesk

First thing you need to do is update to Plesk 12.5 (currently 12.5.30). For that you have to go to Server options, “Plesk->Updates and Upgrades” and then go to “Install or Upgrade Product”. You may get some warnings in case you installed third party modules so you should better remove them (it happended to me with mod_security) before doing the upgrade. As you can see I’ve already updated to 12.5.30 so I can’t show you the proper image but the options are just the same.

upgrade_plesk_product

Once you have Plesk updated the actual magic, in Linux systems, is done with this command (runned as root):

# plesk sbin autoinstaller --select-product-id plesk --select-release-current --install-component php7.0

Full PHP 7.0 install process log: http://pastebin.com/UjH9g4Ak

The nice thing about it is that you can just change the PHP version number to install another one, for example if you have a domain with an app that needs to run in a legacy PHP 5.2 version:

# plesk sbin autoinstaller --select-product-id plesk --select-release-current --install-component php5.2

Now you can select an specific PHP version for different domains by just setting it in the “PHP Settings” option. Before:

php_before

Changing the PHP version to 7.0:

php_70_change

After:

php_after

Ref: https://kb.odin.com/en/127701
https://kb.odin.com/en/125146