Tuesday, November 26, 2013

Tip: How to Add Timestamp With CURRENT_DATETIME in Doctrine 2 ORM

Assuming that your database profiler is Mysql:
    /**
     * @ORM\Column(type="datetime", nullable=false)
     * @ORM\Version
     * @var string
     */
    protected $creationTimestamp;

The above declaration should produce:
CREATE TABLE mytable (creationTimestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL);
The trick is adding @Version which converts the type 'datetime' to 'timestamp'. Remember that this is only for MySql profiler.

Wednesday, November 20, 2013

How to use ZF2's Curl Http Adapter with Header information

Just sharing some of my painful WTF experiences using ZF2's Curl Http client adapter so you wont have to deal with my headaches.

Basically I am trying to send a CURL_HTTPHEADER with these params.
This is what it should look like straight php:

$c = curl_init('http://url');
curl_setopt($c, CURLOPT_POST, 1);
curl_setopt($c, CURLOPT_POSTFIELDS, $data);
curl_setopt($c, CURLOPT_HTTPHEADER, array(
    'Content-type: application/json',
    'Authorization: Bearer 1jo41324knj23o'
));
Simple enough right? Well I was assuming the same thing using ZF2's Curl Adapter:
$client = new Client('http://url');
$client->setMethod('post');

$adapter = new Curl()
$adapter->setCurlOption(CURLOPT_POST, 1);
$adapter->setCurlOption(CURLOPT_POSTFIELDS, $data);
$adapter->setCurlOption(CURLOPT_HTTPHEADER, array(
    'Content-type: application/json',
    'Authorization: Bearer 1jo41324knj23o'
));
Well the thing is this will not work because the headers and data are being set exclusively by write() in the client object as you will see in the source:
// inside Curl::write()
// line 374 Curl.php
curl_setopt($this->curl, CURLOPT_HTTPHEADER, $curlHeaders);

// line 380 Curl.php
if ($method == 'POST') {
    curl_setopt($this->curl, CURLOPT_POSTFIELDS, $body);
}

// line 398 Curl.php
if (isset($this->config['curloptions'])) ...
    foreach ((array) $this->config['curloptions'] as $k => $v) ...
 
Notice at line 398 that this is where the remaining curl parameters gets set. This means that CURLOPT_HTTPHEADER and CURLOPT_POSTFIELDS have already been defined so our usage from above will not work (Maybe there is a CURL flag to overwrite this, I just don't know at the moment). So how do you make this work? You have to pass your definitions in the Client object:
$client = new Client('http://url');
$client->setMethod('post');
$client->setRawBody($data);
$client->setHeaders(array(
    'Content-Type: application/json',
    'Authorization: Bearer 1jo41324knj23o',
));
$client->setAdapter(new Curl());
$client->send();
Now the write() method will pull from these params when assembled

Saturday, November 16, 2013

Automagic Mod_Vhost_Alias for Vagrant precise PHP

Just sharing my Apache vhost to enable automatic virtual hosting for my development environment.
This will enable anything you put to '/vagrant/www/*' a vhost with a public directory. Useful if you are working with frameworks that redirects everything to public/index.php (e.g. Zend Framework)
A directory like:
vagrant/
    www/
        mysite/
            public/
        myothersite/
            public/

Will automatically setup:
1. http://mysite.local
2. http://myothersite.local

And here is the configuration:
<Virtualhost *:80>
    VirtualDocumentRoot "/vagrant/www/%-2+/public"
    ServerName vhosts.local
    ServerAlias *.local
    UseCanonicalName Off
    LogFormat "%V %h %l %u %t \"%r\" %s %b" vcommon

    ErrorLog  /var/log/apache2/local-error_log
    CustomLog /var/log/apache2/local-access_log common

    <Directory ~ "/vagrant/www/[a-z0-9_]+/public">
        Options Indexes FollowSymLinks MultiViews
        AllowOverride All
        Order Allow,Deny
        Allow from all

        <IfModule mod_rewrite.c>
            RewriteEngine On
            RewriteBase /
            RewriteCond %{REQUEST_FILENAME} -s [OR]
            RewriteCond %{REQUEST_FILENAME} -l [OR]
            RewriteCond %{REQUEST_FILENAME} -d
            RewriteRule ^.*$ - [NC,L]
            RewriteCond %{REQUEST_URI}::$1 ^(/.+)(.+)::\2$
            RewriteRule ^(.*) - [E=BASE:%1]
            RewriteRule ^(.*)$ %{ENV:BASE}index.php [NC,L]
        </IfModule>
    </Directory>
</Virtualhost>

Monday, November 4, 2013

How to use ZF2's Service Manager Factories with Constructor Based Objects Returning New Instances

Say you have a manufacturer that produces books and you have multiple new instances of books with different title being manufactured. How would you do this via the Service Manager?

Your book factory accepts a title in its constructor.
class Book
{
    protected $title;

    public function __construct($title)
    {
        $this->title = $title;
    }
}
Now the trick is inside the factory. You need to return a closure inside of a closure like bellow in order to pass a new '$title' and a new instance of book. 
//module.php
'factories' => array(
    'BookFactory' => function ($sm) {
        return function ($title) {
             return new Book($title);
        }
    }
),
In your Manufacturer object.
class BookManufacturer implements ServiceLocatoryAwareInterface
{
    protected $title = array('Lord of the Rings', 'Wheel of Time');

    public function manufacture($qty)
    {
       $bookFactory = $this->getServiceLocator()->get('BookFactory');

       $books = array();
       for ($i = 0; $i <= $qty; ++$i) {
           $books[] = $bookFactory($this->title[rand(0, count($this->title) - 1)]);
       }

       return $books;
    }
}
Or simplified example where you just want a new instance of Book.
// controller
$bookFactory = $this->getServiceLocator()->get('BookFactory');
$book1 = $bookFactory('Lord of the Rings');
$book2 = $bookFactory('Wheel of Time');

var_dump($book1); // new instance of Book with 'Lord of the rings title'
var_dump($book2); // new instance of Book with 'Wheel of time Title'
Does this smells like functional programming?