Invenzzia » Resources / Articles / A photo gallery tutorial / Image preview

9. Image preview

The photo gallery must also provide a photo preview together with the user comments and we are going to implement it right now. Save the following PHP code to /actions/preview.php:

function action()
{
    // Validate the data
    if(!isset($_GET['id']) || !ctype_digit($_GET['id']))
    {
        $view = new Opt_View('message.tpl');
        $view->title = 'Message';
        $view->message = 'Invalid action call!';
        $view->redirect = 'index.php';
        return $view;
    }
 
    // Try to load the image
    $photo = Doctrine::getTable('Photo')->find($_GET['id']);
    if(empty($photo))
    {
        $view = new Opt_View('message.tpl');
        $view->title = 'Message';
        $view->message = 'The requested image has not been found.';
        $view->redirect = 'index.php';
        return $view;
    }
 
    // Prepare the view
    $view = new Opt_View('preview.tpl');
    $view->title = 'Image preview "'.$photo->title.'"';
    $view->photo = $photo;
    $view->setFormat('photo', 'Objective');
 
    // Add the comments
    $view->comments = Doctrine_Query::create()
        ->select('id, author, date, content')
        ->from('Comment')
        ->where('photo_id = ?', $_GET['id'])
        ->orderBy('id')
        ->execute(array(), Doctrine::HYDRATE_ARRAY);
 
    return $view;
} // end action();

At the beginning, our action checks if the URL contains the image ID we want to display. Then it attempts to retrieve the image row, producing an error message in case of failure. Once we have all the data, we are ready to prepare a view for OPT. We already know the structure $container.item on the template side. So far, we have used them only for arrays, but now you will notice that it can be also used for objects. The $photo template variable is initialized with an object. Even if the templates actually allow to access the objects directly, it is not the best idea from the portability and flexibility point of view. Do we really have to know that a particular variable is an object while writing a template? Usually, we would only like to get and display some data. Let's assume that a couple of weeks later we want to change the objects to arrays due the performance reasons. With the direct object or array access, or templates would have to be rewritten, too. This is why we should use generic containers instead and select the data format with setFormat() method. The advantages:

  1. Templates are separated from the application logic and implementation details.

  2. The refactoring process may require to replace the existing PHP data structures with something else. OPT data formats free us from rewriting the templates.

  3. Data formats are something more than just data types. They are also used to provide concrete algorithms and implementations for sections and other template constructs. It allows us to reimplement them for example, to retrieve the data directly from various system structures. OPT itself provides two different algorithms for nested sections, implemented with data formats.

  4. No matter what data format we want to use, the template code remains the same.

  5. The template code can be easily copied between two parts of the application or event between the projects. In the new place, we configure the data formats once again and the code works without any modifications.

Let's take a look at the template (/templates/preview.tpl):

<?xml version="1.0"?>
<opt:extend file="layout.tpl">
<opt:snippet name="content">
    <p><img parse:src="'photos/'~$photo.filename" /></p>
    <p>Added: {$photo.date_text}</p>
    <h3>Comments</h3>
    <opt:show name="comments">
        <div class="comment" opt:section="comments">
            <p class="author">Written by {$comments.author} on {$comments.date_text}</p>
            <p>{$comments.content}</p>
        </div>
    <opt:showelse><p>No comments added.</p></opt:showelse>
    </opt:show>
 
    <p><a parse:href="'index.php?action=comment&id='~$photo.id">Add a comment</a></p>
    <p><a href="'index.php">Back</a></p>
</opt:snippet>
</opt:extend>

This template also shows another type of section – opt:section, provided as a tag attribute. The attribute forms of the instructions are just a syntactic sugar and simplify the code. The instruction itself is used to display linear lists with the elements shown one after another and it is the most commonly used section type. The other aspects of its functionality are the same, as in opt:grid. The section data for comments are retrieved directly from Doctrine. The model automatically formats the date for us and we can access it with {$comments.date_text}.