Welcome to Abdul Malik Ikhsan's Blog

Zend Framework 2 : Using DoctrineModule\Form\Element\ObjectSelect and custom repository

Posted in Tutorial PHP, Zend Framework 2 by samsonasik on May 22, 2014

zf2-zendframework2When we are using Doctrine2 in Zend Framework 2 project, we can use existing module named DoctrineModule that can be used to easist our job. Now I will explain about how to use DoctrineModule\Form\Element\ObjectSelect in our Form to load data into select element with custom query instead of default one.
For example, we have data like this :
countries-continent
Now, we need to build a form element that collect continent data, that’s means, we need to ‘group by continent’ for query-ing the table data like this :
continent-grab
To make it work, we need to create custom repository. Ok, let’s start.
1. Prepare the Entity

//module/Tutorial/src/Tutorial/Entity/Countries.php
namespace Tutorial\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * Countries
 *
 * @ORM\Table(name="countries")
 * @ORM\Entity(repositoryClass="Tutorial\Repository\CountriesRepository")
 */
class Countries
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="bigint", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="country", type="string", length=30, nullable=false)
     */
    private $country;

    /**
     * @var string
     *
     * @ORM\Column(name="continent", type="string", length=30, nullable=false)
     */
    private $continent;



    /**
     * Get id
     *
     * @return integer 
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set country
     *
     * @param string $country
     * @return Countries
     */
    public function setCountry($country)
    {
        $this->country = $country;

        return $this;
    }

    /**
     * Get country
     *
     * @return string 
     */
    public function getCountry()
    {
        return $this->country;
    }

    /**
     * Set continent
     *
     * @param string $continent
     * @return Countries
     */
    public function setContinent($continent)
    {
        $this->continent = $continent;

        return $this;
    }

    /**
     * Get continent
     *
     * @return string 
     */
    public function getContinent()
    {
        return $this->continent;
    }
}

Above entity is pretty generic, but we add new “repositoryClass” attribute in the @ORM\Entity annotation to linked with our custom repository.
2. Create a custom repository

//module/Tutorial/src/Tutorial/Repository/CountriesRepository.php
namespace Tutorial\Repository;

use Doctrine\ORM\EntityRepository;

class CountriesRepository extends EntityRepository
{
    public function getContinent()
    {
        $querybuilder = $this->createQueryBuilder('c');
        return $querybuilder->select('c')
                    ->groupBy('c.continent')
                    ->orderBy('c.id', 'ASC')
                    ->getQuery()->getResult();
    }
}

The getContinent grab the countries data group by continent.
3. Create form

//module/Tutorial/src/Tutorial/Form/CountriesForm.php
namespace Tutorial\Form;

use Zend\Form\Form;
use Zend\InputFilter\InputFilterProviderInterface;
use Doctrine\ORM\EntityManager;

class CountriesForm extends Form
    implements InputFilterProviderInterface
{
    protected $entityManager;
    
    public function __construct(EntityManager $entityManager)
    {
        parent::__construct();

        $this->entityManager = $entityManager;       
    }
    
    public function init()
    {
        $this->add(array(
           'name' => 'continent',
           'type' => 'DoctrineModule\Form\Element\ObjectSelect',
           'options' => array(
                'object_manager'     => $this->entityManager,
                'target_class'       => 'Tutorial\Entity\Countries',
                'property' => 'continent',
                'is_method' => true,
                'find_method'        => array(
                    'name'   => 'getContinent',
                ),
            ), 
        ));
    }
    
    public function getInputFilterSpecification()
    {
        return array(); // filter and validation here
    }
}

We need to inject form with Doctrine\ORM\EntityManager, and call the getContinent method that we already define at our CountriesRepository. So the Form need to be created via factory :

//module/Tutorial/src/Tutorial/Factory/Form/CountriesFormFactory.php
namespace Tutorial\Factory\Form;

use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Tutorial\Form\CountriesForm;

class CountriesFormFactory implements FactoryInterface
{
    public function createService(ServiceLocatorInterface $serviceLocator)
    {
        $services         = $serviceLocator->getServiceLocator();
        $entityManager    = $services->get('Doctrine\ORM\EntityManager');
        
        $form = new CountriesForm($entityManager);
        return $form;
    }
}

4. Create controller

//module/Tutorial/src/Tutorial/Controller/CountriesController.php
namespace Tutorial\Controller;

use Zend\Mvc\Controller\AbstractActionController;
use Zend\Form\FormInterface;
use Zend\View\Model\ViewModel;

class CountriesController extends AbstractActionController
{
    protected $countriesForm;
    
    public function __construct(FormInterface $countriesForm)
    {
        $this->countriesForm = $countriesForm;
    }
    
    public function indexAction()
    {
        return new ViewModel(array(
            'form' => $this->countriesForm, 
        ));
    }
}

We need to inject controller with the CountriesForm, so the Controller need to be created via factory :

//module/Tutorial/src/Tutorial/Factory/Controller/CountriesControllerFactory.php
namespace Tutorial\Factory\Controller;

use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Tutorial\Controller\CountriesController;

class CountriesControllerFactory implements FactoryInterface
{
    public function createService(ServiceLocatorInterface $serviceLocator)
    {
        $services       = $serviceLocator->getServiceLocator();
        $countryForm    = $services->get('FormElementManager')->get('Tutorial\Form\CountriesForm');
        $controller = new CountriesController($countryForm);

        return $controller;
    }
}

5. Register services

//module/Tutorial/config/module.config.php
return array(
    'doctrine' => array(
        'driver' => array(
            'Tutorial_Entities' => array(
                'class' =>'Doctrine\ORM\Mapping\Driver\AnnotationDriver',
                'cache' => 'array',
                'paths' => array(__DIR__ . '/../src/Tutorial/Entity')
            ),
            'orm_default' => array(
                'drivers' => array(
                    'Tutorial\Entity' => 'Tutorial_Entities'
                ),
            ),
        ),
    ),
    
    'controllers' => array(
        'factories' => array(
            'Tutorial\Controller\Countries' => 'Tutorial\Factory\Controller\CountriesControllerFactory',  
        ),
    ),
    
    'form_elements' => array(
        'factories' => array(
            'Tutorial\Form\CountriesForm' => 'Tutorial\Factory\Form\CountriesFormFactory',  
        ),
    ),
    
    'router' => array(
        'routes' => array(
            'countries' => array(
                'type'    => 'segment',
                'options' => array(
                    'route'    => '/countries[/:action]',
                    'constraints' => array(
                        'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
                    ),
                    'defaults' => array(
                        'controller' => 'Tutorial\Controller\Countries',
                        'action'     => 'index',
                    ),
                ),
            ),
        ),
    ),
    
    'view_manager' => array(
        'template_path_stack' => array(
            'tutorial' => __DIR__ . '/../view',
        ),
    ),
    
);

6. Last but not least, build a view :

// module/Tutorial/view/tutorial/countries/index.phtml
$form = $this->form;
$form->prepare();

echo $this->form()->openTag($form);
echo $this->formCollection($form);
echo $this->form()->closeTag();

Ok, done 😉

Zend Framework 2 : Getting real Sql String of Zend\Db

Posted in Tutorial PHP, Zend Framework 2 by samsonasik on May 16, 2014

zf2-zendframework2 I know, this is maybe a simple post for you, but it useful for me :). Hope it useful for someone. When we build a query using Zend\Db, sometime, complex query need checking for debugging to make sure our query is right. For example, we need to make sure we build a query like this :

SELECT `album`.* FROM `album` WHERE `title` = 'abracadabra' AND (`id` = '1' OR `artist` = 'Tony') LIMIT 1

And we already have an \Zend\Db\Sql\Select instance. In case if you have Model class like zf2 docs provide

$sql = $this->tableGateway->getSql();
$select = $sql->select();
$select->where(array('title' => 'abracadabra'));
$select->where
  ->NEST->
        equalTo('id', 1)
            ->OR->
        equalTo('artist', 'Tony')
  ->UNNEST;

$select->limit(1);

So, Before we return it with :

return $this->tableGateway->selectWith($select);

We can check it with :

echo $sql->getSqlstringForSqlObject($select); die ; // ( die/exit to debugging purpose )
//it will print sql string :
// SELECT `album`.* FROM `album` WHERE `title` = 'abracadabra' AND (`id` = '1' OR `artist` = 'Tony') LIMIT 1

That will return the real sql build for our specific platform.

references :
https://github.com/zendframework/zf2/issues/3224

Don’t push yourself

Posted in Psychology by samsonasik on May 8, 2014

When I was a child, I wished I will be an army. And.. it is not granted. In sport, I’m not good at all. So, it will not be good if I push myself to make it happen. Albert Einstein said, “Everybody is a genius. But if you judge a fish by it’s ability to climb a tree, it will live its whole life believing that it is stupid.”. This realized me that I should do what I can and better at that part. From highschool, I like to write poems. That’s my ability. I’m not saying that I’m good on writing poems, but I can say that it’s better for me to write a poem rather than push myself to do sport that I can’t handle. Now, I’m a web developer, it is better to me to code rather than to do networking job.
It doesn’t means that you should surrender at your condition, but you should understand that you have things that if you do it well, it will be great for you. So, know your weakness, know your strong suit. Do what your best ability.

reference :
image : https://encrypted-tbn1.gstatic.com/images?q=tbn:ANd9GcQkwkta-0K4qeu9QkMNw6J1p_sHaIiq7lHiLMfpTHlLxFE9nZ5B
quote : http://www.goodreads.com/quotes/101458-everybody-is-a-genius-but-if-you-judge-a-fish