Working with the PHP Laravel Framework

laravel-logo-bigI’ve worked with Laravel for about a year and half now and it’s the third PHP framework I’ve  learned so far (the others are Code Iginiter (2 projects) and Zend (1 project).

So, what’s to like about Laravel and what are the niggles?

As a coder, there’s lots to like about Laravel. The code is clean and well written. It follows all the best Object Oriented practices, uses patterns and is generally easy to read.

Niggles: It’s definitely the pet project of the guy who created it, Jeffrey Way. That means whatever he decides goes. Expect some quite quirky bits of code.

The main niggle, which also happened with Zend, is the project structure has changed in a way to make an upgrade really complicated. With Zend, the upgrade from Zend 1.2 to Zend 2.0 meant that you had to substantially re-write your project. This was a major annoyance on lots of levels. It increased the cost to the client and it also meant I had to learn Zend again.

Having written a fair amount of code in Laravel 4.2 for a client, I took a look to see if I could easily update it to Laravel 5.1 Answer, not at all. I took nearly a whole day to experiment on this and only managed to get some basic pages up.

I’ve written a simple Laravel 5 project to demo my code. There wasn’t that much to relearn once I had got used to the project structure. The major thing I can see is the addition of middleware.

Laravel is still a great framework to work with and I look forward to doing further work on it.

Zend Framework: Adding XHTML within a form

I recently needed to add some custom HTML within a form. It proved to be easy but I did need to write some custom code to do it.

First, I had to create a HTML form element by subclassing Zend_Form_Element

class Misc_FormHtml extends Zend_Form_Element_Xhtml{
 /**
     * Load default decorators
     *
     * @return Zend_Form_Element
     */
    public function loadDefaultDecorators()
    {
        if ($this->loadDefaultDecoratorsIsDisabled()) {
            return $this;
        }

        $decorators = $this->getDecorators();
        if (empty($decorators)) {
            $getId = create_function('$decorator',
                'return $decorator->getElement()->getId()
                        . "-element";');
            $this ->addDecorator(new Decorators_Html())
                ->addDecorator('HtmlTag', array('tag' => 'div',
                    'id'  => array('callback' => $getId)))
           ;
        }
        return $this;
    }
}

Zend loads the decorators from the inside out so in order to wrap my HTML in a div tag, I need to add a custom decorator (new Decorators_Html() ) first and then add the HtmlTag decorator second

I wrote a very simple decorator.

class Decorators_Html  extends Zend_Form_Decorator_Abstract
{
    public function render($content)
    {
       //return $content;
        $element = $this->getElement();
        $value = $element->getValue();
        return $content.$value ;
    }

}

Then I set it up to be used:

... form code
 $html = new Misc_FormHtml();
        $html->setValue('

Here is some text that we are really hoping will appear on the page without being messed up by Zend

') ->setAttrib('id','top_form_text'); $category_sub_form->addElement($html);

Zend Framework: Uploading and resizing an image

I’ve needed to upload and resize an image on a Zend Framework. Not especially difficult but it does take a little setting up.

First, I need to set up some upload locations for the final files. This was in my application.ini file like this:

paths.images.thumbs=APPLICATION_PATH "/../public/photos/thumbs"
paths.images.web=APPLICATION_PATH "/../public/photos/web"
paths.images.original=APPLICATION_PATH "/../public/photos/original"

Then I needed to set up the form

class Application_Form_Photos extends Zend_Form
{

    public function init()
    {
        $decorator = new Decorators_Inputs();

        // Add a name element
        $this->addElement('text', 'name', array(
            'label' => 'Photo Name:',
            'required' => true,
            'style' => 'width:200px;',
            'filters' => array('StringTrim'),
            'validators' => array(
                'NotEmpty'
            ),
            'decorators' => array($decorator)
        ));

        $photo = new Zend_Form_Element_File('photo');
        $photo->setLabel('Photo (jpg only)')
            ->setDestination(Zend_Registry::get('config')->paths->images->original);
        // ensure only one file
        $photo->addValidator('Count', false, 1);
        // max 10MB
        $photo->addValidator('Size', false, 10097152)
            ->setMaxFileSize(10097152);
        // only JPEG, PNG, or GIF
        $photo->addValidator('Extension', false, 'jpg,png,gif');
        $photo->setValueDisabled(true);
        $photo->setDecorators(array('File', new Decorators_Files()));
        $this->addElement($photo, 'photo');

        $submit = new Zend_Form_Element_Submit('submit');
        $submit->setValue('Upload');
        $submit->setName('Upload');
        $submit->setDecorators(array(new Decorators_Submit()));
        $this->addElement($submit);

    }

}

The Decorators_Files class is a decorator (See here for how to set that up)

class Decorators_Files extends Zend_Form_Decorator_Abstract
{
    protected $_format = '
    
%s 

'; public function render($content) { $element = $this->getElement(); $name = htmlentities($element->getFullyQualifiedName()); $label = htmlentities($element->getLabel()); $id = htmlentities($element->getId()); $value = htmlentities($element->getValue()); $style = htmlentities($element->getAttrib('style')); $class = htmlentities($element->getAttrib('class')); $error = ""; $errors = $element->getErrors(); if($errors){ $error = 'Please enter a value'; $class = 'error'; } $markup = sprintf($this->_format, $name, $label, $id, $name, $value,$class, $style,$error); return $markup; } }

Now, I need the code to handle the upload

/**
     * Handle the image upload
     * @param Application_Form_Photos $form, the form that contains the image
     * @param Zend_Controller_Request_Abstract $request, the request
     * @param Application_Model_MediaMapper $media_mapper, the Model class which handles saving the file details
     */
    private function handle_image_upload(Application_Form_Photos $form, Zend_Controller_Request_Abstract $request, Application_Model_MediaMapper $media_mapper)
    {
       //Create an entry in the DB
        $extension = pathinfo($form->getElement('photo')->getValue(), PATHINFO_EXTENSION);
        $media_model = new Application_Model_Media();
        $media_model->setName($request->getParam('name'))
            ->setThumb('')
            ->setWeb('')
            ->setOriginal('');
        $media_mapper->save($media_model);

       //Rename the file
        $form->getElement('photo')->addFilter('Rename', array(
            'target' => $media_model->getId() . '.' . $extension,
            'overwrite' => true
        ));

        //Add the filter to resize to web image
        $form->getElement('photo')->addFilter(new Filters_File_Resize(array(
            'width' => 800,
            'height' => 600,
            'keepRatio' => true,
            'directory' => Zend_Registry::get('config')->paths->images->web
        )));

        //Add the filter to resize to thumb, note it can't be the same filter as above or the web image action will fail
        $form->getElement('photo')->addFilter(new Filters_File_Thumb(array(
            'width' => 200,
            'height' => 133,
            'keepRatio' => false,
            'directory' => Zend_Registry::get('config')->paths->images->thumbs
        )));

        //Upload the photo
        if ($form->getElement('photo')->receive()) {
            $media_model->setThumb('photos/thumbs/' . $media_model->getId() . '.' . $extension);
            $media_model->setWeb('photos/web/' . $media_model->getId() . '.' . $extension);
            $media_model->setOriginal('photos/original/' . $media_model->getId() . '.' . $extension);
            $media_mapper->save($media_model);
        }
    }

Finally, I need some filters that will handle the image resize.

I initially used the filter written by Stefan Koch. See
http://eliteinformatiker.de/2011/09/02/thumbnails-upload-and-resize-images-with-zend_form_element_file/

Zend Framework, styling and decorating a form

Although extending Zend_Form produces a really easy form, the layout that results is less than perfect. This is where subclassing Zend_Form_Decorator_Abstract comes in. Here’s the implementation for a text field.

class Decorators_Inputs extends Zend_Form_Decorator_Abstract
{
    protected $_format = '
    <div class="element">
        <div class="label_col"
            <label for="%s">%s</label>
        </div>
         <div class="input_col">
            <input id="%s" name="%s" type="text" value="%s" class="%s" style="%s"/>
         </div>
         <div class="error_col">
            %s 
         </div>
     </div>
     <br clear="left" />
     ';

    public function render($content)
    {
        $element = $this->getElement();
        $name    = htmlentities($element->getFullyQualifiedName());
        $label   = htmlentities($element->getLabel());
        $id      = htmlentities($element->getId());
        $value   = htmlentities($element->getValue());
        $style   = htmlentities($element->getAttrib('style'));

        $class = "";
        $error = "";
        $errors = $element->getErrors();
        if($errors){
            $error = 'Please enter a value';
            $class = 'error';
        }

        $markup  = sprintf($this->_format, $name, $label, $id, $name, $value,$class, $style,$error);
        return $markup;
    }
}

Zend framework, sending an email

Sending an email in the Zend Framework turns out to be reasonably easy. This example uses the contact form for a web site

public function contactAction()
    {
        $request = $this->getRequest();
        $form = new Application_Form_Contact();

       //Check if form has been submitted
        if ($this->getRequest()->isPost()) {
            if ($form->isValid($request->getPost())
              && $this->send($this->getRequest())) {

                $this->_forward('thanks');
            }
        }

        $this->view->form = $form;
    }

    private function send(Zend_Controller_Request_Abstract $request)
    {

        $session = new Zend_Session_Namespace();

        if (isset($session->numberOfPageRequests)) {
            $session->numberOfPageRequests++;
        } else {
            $session->numberOfPageRequests = 1;
        }

        //pretend mail has been sent for people repeatedly hitting refresh
        if ($session->numberOfPageRequests > 2) {
            return true;
        }

        $email_mapper = new Application_Model_EmailMapper();
        //Get the email text from the database.
        $email = $email_mapper->getEmailByName('ContactEmail', $request);

        $mail = new Zend_Mail();
        $mail->setBodyText($email->getText());
        $mail->setBodyHtml($email->getHtml());
        $mail->setFrom('somebody@example.com', 'Some Sender');
        $mail->addTo('somebody_else@example.com', 'Some Recipient');
        $mail->setSubject('TestSubject');
        $mail->send();

        return true;

    }

This is the result of sending the email

I’m using Toolheap’s Test email SMTP server to send test emails during development. It’s proven to be a very useful tool for verifying designs and content of the emails before they are put live.

Zend Framework, creating a contact form

Creating a contact form is quite straightforward in the Zend Framework.

zf create form Contact

zf create controller Form

zf create view Form

Specify the form in the Application_Form_Contact class in application/forms/Contact.php

class Application_Form_Contact extends Zend_Form
{

    public function init()
    {
        // Set the method for the display form to POST
        $this->setMethod('post');

        // Add a name element
        $this->addElement('text', 'name', array(
            'label' => 'Your name:',
            'required' => true,
            'filters' => array('StringTrim'),
            'validators' => array(
                'NotEmpty',
            )
        ));

        $this->addElement('text', 'phone', array(
            'label' => 'Your phone:',
            'required' => false,
            'filters' => array('StringTrim'),

        ));

        // Add an email element
        $this->addElement('text', 'email', array(
            'label' => 'Your email address:',
            'required' => true,
            'filters' => array('StringTrim'),
            'validators' => array(
                'EmailAddress',
            )
        ));

        // Add the comment element
        $this->addElement('textarea', 'comment', array(
            'label' => 'Please Comment:',
            'required' => true,
            'validators' => array(
                array('validator' => 'StringLength', 'options' => array(0, 20))
            )
        ));

        // Add a captcha
        $this->addElement('captcha', 'captcha', array(
            'label' => 'Please enter the 5 letters displayed below:',
            'required' => true,
            'captcha' => array(
                'captcha' => 'Figlet',
                'wordLen' => 5,
                'timeout' => 300
            )
        ));

        // Add the submit button
        $this->addElement('submit', 'submit', array(
            'ignore' => true,
            'label' => 'Sign Guestbook',
        ));

        // And finally add some CSRF protection
        $this->addElement('hash', 'csrf', array(
            'ignore' => true,
        ));
    }

}

Write an action in application/controllers/FormController.php

public function contactAction()
    {
        $request = $this->getRequest();
        $form = new Application_Form_Contact();
        if ($this->getRequest()->isPost()) {
            if ($form->isValid($request->getPost())) {
                echo 'OK';
            }
        }

        $this->view->form = $form;
    }

Update the routes so that /contact will get the contact form in /Bootstrap.php in the _initRoutes function. Note: the routes are matched in reverse order so the most generic one has to be first.

$frontController = Zend_Controller_Front::getInstance();
        $router = $frontController->getRouter();

        $contact = new Zend_Controller_Router_Route('contact',array('controller'=>'Form',
 'action'=>'contact'));

        $route_content = new Zend_Controller_Router_Route_Regex(
            '(\w*)',
            array('controller' => 'content',
                'action' => 'index' ),
            array(1 => 'url')
        );

        try{
            $router->addRoute('content', $route_content);
            $router->addRoute('form', $contact);
        }catch(Exception $e) {
            print($e->getTraceAsString());
        }

All fine, this is what I get:

Zend Framework, setting up a dynamic menu using a plugin

I wanted to set up the menu in layout.phtml using a dynamic call to the database.

I created a directory in library called plugins.

I put this in application.ini
autoloaderNamespaces[] = “Plugins”

I created a class in the plugins directory called Plugins_Layout with the file name Layout.php

class Plugins_Layout extends Zend_Controller_Plugin_Abstract
{
    public function preDispatch(Zend_Controller_Request_Abstract $request)
    {
        $layout = Zend_Layout::getMvcInstance();
        $view = $layout->getView();

        $content_mapper = new Application_Model_ContentMapper();

        $view->menu = $content_mapper->getMenu();
    }
}

I then added this to Bootstrap.php

protected function _initRoutes() {
        ...... 

       Zend_Controller_Front::getInstance()->registerPlugin(new Plugins_Layout());
    }

I was then able to create the dynamic menu in layout.phtml

 menu as $url => $title) { ?>
    

All was working as I expected.

Zend framework, mapping urls in the database

Having set up the application and got it to serve a page from the database, I need to make sure that it was getting the content I wanted.

I wanted to have something like http://localhost/my_title find the content from the url field with value my_title

I added this to my Bootstrap.php

protected function _initRoutes() {
        $frontController = Zend_Controller_Front::getInstance();
        $router = $frontController->getRouter();

        $route = new Zend_Controller_Router_Route_Regex(
            '(\w*)',
            array('controller' => 'content',
                'action' => 'index' ),
            array(1 => 'url')
        );


        try{
           $router->addRoute('content', $route);
        }catch(Exception $e) {
            print($e->getTraceAsString());
        }

    }

This mapped to the ContentController class with the following code

public function indexAction()
    {
        // action body
        $content_mapper = new Application_Model_ContentMapper();

        $url = $this->getRequest()->getParam('url');
        $content = new Application_Model_Content();
        $content_mapper->findByUrl($url, $content);
        $this->view->content = $content;

    }

Zend framework, database connection

I set up a database connection in application/config/application.ini

resources.db.adapter = "PDO_MYSQL"
resources.db.params.host = "localhost"
resources.db.params cialis preise holland.username = "username"
resources.db.params.password = "password"
resources.db.params.dbname = "montesnegro"

Next I created models:

zf create db-table Content content
zf create model ContentMapper
zf create model Content
zf create controller Content

I created the PHP for the Content model in application/models/content

class Application_Model_Content
{
    private $id;
    private $menu_id;
    private $content_text;
    private $update_date;

    function __construct($id = null,$menu_id = null,$content_text = null,$update_date = null)
    {
        $this->id = $id;
        $this->menu_id = $menu_id;
        $this->content_text = $content_text;
        $this->update_date = $update_date;
    }


    public function setContentText($content_text)
    {
        $this->content_text = $content_text;
        return $this;
    }

    public function getContentText()
    {
        return $this->content_text;
    }

    //other getters and setters.....
}

I created a ContentMapper class

class Application_Model_ContentMapper
{

    /**
     * @var Zend_Db_Table_Abstract
     */
    protected $_dbTable;

    public function setDbTable($dbTable)
    {
        if (is_string($dbTable)) {
            $dbTable = new $dbTable();
        }
        if (!$dbTable instanceof Zend_Db_Table_Abstract) {
            throw new Exception('Invalid table data gateway provided');
        }
        $this->_dbTable = $dbTable;
        return $this;
    }

    public function getDbTable()
    {
        if (null === $this->_dbTable) {
            $this->setDbTable('Application_Model_DbTable_Content');
        }
        return $this->_dbTable;
    }

    public function save(Application_Model_Content $content)
    {
        $data = array(
            'content_text'   => $content->getContentText(),
            'menu_id' => $content->getMenuId(),
            'update_text' => date('Y-m-d H:i:s'),
        );

        if (null === ($id = $content->getId())) {
            unset($data['id']);
            $this->getDbTable()->insert($data);
        } else {
            $this->getDbTable()->update($data, array('id = ?' => $id));
        }
    }

    public function getMenu()
    {
        $select = $this->getDbTable()->select()->order("order ASC ");
        $results = $this->getDbTable()->fetchAll($select);
        $menu   = array();
        foreach ($results as $row) {
            $menu[$row->url] = $row->title;
        }
        return $menu;

    }

    public function find($id, Application_Model_Content $content)
    {
        $result = $this->getDbTable()->find($id);
        if (0 == count($result)) {
            return;
        }
        $this->setContent($result->current(), $content);
    }

    public function findByUrl($url,Application_Model_Content $content)
    {
        if(!$url){
            $url = 'home';
        }
        $select = $this->getDbTable()->select()->where("url = '$url' ");
        $result = $this->getDbTable()->fetchRow($select);
        if (0 == count($result)) {
            return;
        }
        $this->setContent($result->toArray(), $content);
    }

    private function setContent($row, Application_Model_Content $content)
    {
        $content->setId($row['id'])
            ->setContentText($row['content_text'])
            ->setMenuId($row['menu_id'])
            ->setUpdateDate($row['update_date'])
            ->setUrl($row['url'])
            ->setTitle($row['title'])
        ;
    }

    public function fetchAll()
    {
        $resultSet = $this->getDbTable()->fetchAll();
        $entries   = array();
        foreach ($resultSet as $row) {
            $entry = new Application_Model_Content();
            $this->setContent($row, $entry);
            $entries[] = $entry;
        }
        return $entries;
    }

}

And then I created the controller

class ContentController extends Zend_Controller_Action
{

    public function init()
    {
        /* Initialize action controller here */
    }

    public function indexAction()
    {
        // action body
        $content_mapper = new Application_Model_ContentMapper();

        $url = $this->getRequest()->getParam('url');
        $content = new Application_Model_Content();
        $content_mapper->findByUrl($url, $content);
        $this->view->content = $content;

    }


}

Finally, I created a MySQL table called content and put some stuff in there.

When I did http://localhost/content, all was well.

Zend framwork, layout

I have set up the Zend Framework and had it serving up the default page. Now it was time to set up a layout.

zf enable layout

Then I put in the HTML for application/layout/scripts/layout.phtml

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Montes Negros</title>
<?php echo $this->headLink()->appendStylesheet('/css/main.css') ?>
</head>

<body>
<div id="background_layer"><img src="/images/background.jpg" alt="" width="100%" /></div>
<div id="container">
<div id="allcontent">
<div id="header">
<div id="header_text">
<h1>Casa Montes Negros</h1>
<h2>Rural Organic Rental Cottage</h2>
</div>

</div>
<div id="left_menu">
<p>Menu</p>
<p>Menu</p>
<p>Menu</p>
<p>Menu</p>
<p>Menu </p>
</div>

<div id="main"><?php echo $this->layout()->content ?></div>

<div id="footer">&copy;Montes Negros 2012</div>
</div>
</div>
</body>
</html>

I updated application/views/scripts/index/index.phtml with some default text. This gave me: