jump to navigation

Tutorial: Web based search engine for Flickr June 4, 2006

Posted by rossoft in CakePHP.
110 comments

This is a CakePHP clone of the screencast of RoR (they can, we can).

The source code and related files can be found here.

0. Download cake, untar it in a directory, configure your webserver, set write permission to app/tmp/ recursive
If you don’t have mod_rewrite, uncomment     define (’BASE_URL’, env(’SCRIPT_NAME’)); at app/config/core.php

1. Create app/views/layouts/default.thtml

<?php header(’Content-type: text/html;charset=UTF-8′);?>
<!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=utf-8″ />
    <title><?php echo $title_for_layout ?></title>
    <?php
        echo $javascript->link(’prototype’);
        echo $javascript->link(’scriptaculous’);
        echo $javascript->link(’effects’);
        echo $html->css(’flickr’);
    ?>
    </head>
    <body>
        <?php echo $content_for_layout ?>
    </body>
</html>

2. Create app/views/layouts/ajax.thtml

<?php header(’Content-type: text/html;charset=UTF-8′);?>
<?php echo $content_for_layout; ?>

3. Bake:

rossoft@linux:~/programacion/flickr_tutorial> php cake/scripts/bake.php

Your database configuration was not found. Take a moment to create one:

—————————————————————
Database Configuration Bake:
—————————————————————

What is the hostname for the database server?
[localhost] >

What is the database username?
> XXXXX

What is the database password?
> YYYYY

What is the name of the database you will be using?
> mydatabase

—————————————————————
The following database configuration will be created:
—————————————————————
Host:       localhost
User:       ********
Pass:       ********
Database:   mydatabase
—————————————————————

Look okay? (y/n)
[y] > y

Creating file /home/rossoft/programacion/flickr_tutorial//app/config/database.php
Wrote   /app/config/database.php
[M]odel
[C]ontroller
[V]iew

Please select a class to Bake: (M/V/C)
> C
—————————————————————
Controller Bake:
—————————————————————

Controller name? Remember that Cake controller names are plural.
> Flickr

Would you like bake to build your controller interactively?
Warning: Choosing no will overwrite Flickr controller if it exist. (y/n)
[y] > y

Would you like this controller to use other models besides ‘Flickr’? (y/n)
[n] > n

Would you like this controller to use other helpers besides HtmlHelper? (y/n)
[n] > y

Please provide a comma separated list of the other helper names you’d like to use.
Example: ‘Ajax, Javascript, Time’
> Ajax, Javascript

Would you like this controller to use any components? (y/n)
[n] > y

Please provide a comma separated list of the component names you’d like to use.
Example: ‘Acl, MyNiftyHelper’
> RequestHandler

Would to include some basic class methods (index(), add(), view(), edit())? (y/n)
[n] > n

—————————————————————
The following controller will be created:
—————————————————————
Controller Name:        Flickr
Helpers:                        Ajax, Javascript
Components:            RequestHandler
—————————————————————

Look okay? (y/n)
[y] > y

Creating file /home/rossoft/programacion/flickr_tutorial//app/controllers/flickr_controller.php
Wrote   /app/controllers/flickr_controller.php

Cake test suite not installed.  Do you want to bake unit test files anyway? (y/n)
[y] > n

4. Change the file app/controllers/flickr_controller.php to

<?php
class FlickrController extends AppController
{
        //var $scaffold;
        var $name = ‘Flickr’;
        var $helpers = array(’Html’, ‘Ajax’, ‘Javascript’);
        var $components = array(’RequestHandler’);
        var $uses = array();

        function index()
        {
        }
}
?>

5. rossoft@linux:~/programacion/flickr_tutorial> php cake/scripts/bake.php
Please select a class to Bake: (M/V/C)
> V
—————————————————————
View Bake:
—————————————————————

Controller Name? (plural)
> Flickr

Would you like bake to build your views interactively?
Warning: Choosing no will overwrite  views if it exist. (y/n)
[y] > y

Would you like to create some scaffolded views (index, add, view, edit) for this controller?
NOTE: Before doing so, you’ll need to create your controller and model classes (including associated models). (y/n)
[n] > n

Action Name? (use camelCased function name)
> index
—————————————————————
The following view will be created:
—————————————————————
Controller Name: flickr
Action Name:     index
Path:            app/views/flickr/index.thtml
—————————————————————

Look okay? (y/n)
[y] > y

6. At this point, you can go to your browser http://your_path_to_cake/flickr/ and you will see ‘index’ string
7. Change the file app/views/flickr/index.thtml to :

<?php echo $ajax->form(array(’action’=>$html->url(’search’)))?>
    <fieldset>
        <label for=”tags”>Tags:</label>
        <?php echo $html->input(’Flickr/tags’) ?>
        <?php echo $ajax->submit(’Find’) ?>
        <?php echo $html->image(’spinner.gif’,array(    ‘alt’=>’Searching…’,
                                                ‘id’=>’spinner’,
                                                ’style’=>’display: none’))?>
    </fieldset>
    <div id=”photos” />
    <?php echo $ajax->observeField(’FlickrTags’,
            array(    ‘update’=>’photos’,
                      ‘url’=>’search’,
                    ‘frequency’=>2,
                    ‘loading’=>”Element.show(’spinner’);”,
                    ‘loaded’=>”Element.hide(’spinner’);”,
                    ‘complete’=>”new Effect.BlindDown(’photos’)”,
                    ));
    ?>   
</form>

8. Create app/webroot/css/flickr.css :

body {
    background-color: #888;
    font-family: Lucida Grande;
    font-size: 11px;
    margin: 25px;
}

form {
    margin: 0;
    margin-bottom: 10px;
    background-color: #eee;
    border: 5px solid #333;
    padding: 25px;
}

fieldset {
    border: none;
}

#photos img {
    border: 1px solid #000;
    width: 75px;
    height: 75px;
    margin: 5px;
}

input {
  color: #000000;
  background-color:#ccc;
}

input:hover {
  color: #ffff00;
  background-color:#777;
}

9. Refresh browser to see the changes

10.

rossoft@linux:~/programacion/flickr_tutorial> php cake/scripts/bake.php
Please select a class to Bake: (M/V/C)
> V
Controller Name? (plural)
> Flickr
Would you like bake to build your views interactively?
Warning: Choosing no will overwrite  views if it exist. (y/n)
[y] >
Would you like to create some scaffolded views (index, add, view, edit) for this controller?
NOTE: Before doing so, you’ll need to create your controller and model classes (including associated models). (y/n)
[n] >
Action Name? (use camelCased function name)
> search
—————————————————————
The following view will be created:
—————————————————————
Controller Name: flickr
Action Name:     search
Path:            app/views/flickr/search.thtml
—————————————————————
Look okay? (y/n)
[y] > y
Creating file /home/rossoft/programacion/flickr_tutorial//app/views/flickr/search.thtml
Wrote   /app/views/flickr/search.thtml

11. Replace app/controllers/flickr_controller.php with this:
(added Flickr component and search() method)

<?php
class FlickrController extends AppController
{
    //var $scaffold;
    var $name = ‘Flickr’;
    var $helpers = array(’Html’, ‘Ajax’, ‘Javascript’);
    var $components = array(’RequestHandler’,'Flickr’);
    var $uses = array();
   

    function index()
    {
       
    }   
   
    function search()
    {
        $photos = array();
        if ($search=@$this->data['Flickr']['tags'])
        {
            $data = $this->flickr->photos_search(array(
                                                    ‘tags’ => $search,
                                                    ‘per_page’ => 24));
            if (isset($data['photo']) && is_array($data['photo']))
            {
                foreach ($data['photo'] as $photo)
                {
                    $photos[]=array(
                            ‘info_url’=>”http://www.flickr.com/photos/{$photo['owner']}/{$photo['id']}”,
                            ’small_url’    =>$this->flickr->buildPhotoURL($photo,”small”));                   
                }   
            }
           }
        $this->set(’photos’,$photos);
    }
}   
?>

12. Edit app/views/flickr/search.thtml :

<?php
    foreach ($photos as $photo)
    {           
        echo $this->renderElement(’flickr/photo’,$photo);   
    }
?>

13. Create app/views/elements/flickr/photo.thtml :

<a class=”photo” href=”<?php echo $info_url?>” target=”_blank”>
    <img class=”photo” src=”<?php echo $small_url?>”>
</a>

14. Copy the flickr component to app/controllers/components/flickr.php
Edit and change the API KEY (you can get your own at http://www.flickr.com/services/api/misc.api_keys.html )

15. Copy the phpflickr dir to app/vendors/phpflickr/ (download from https://sourceforge.net/project/showfiles.php?group_id=139987 )
16. Copy the spinner.gif to app/webroot/img
17. Copy scriptaculous + prototype js to app/webroot/js/
18. Refresh your browser and see the magic

Flickr Component June 4, 2006

Posted by rossoft in CakePHP.
6 comments

Flickr component for easy integration of phpFlickr

You will have access to phpflickr instance in your controllers: $this->flickr, and in your views: $flickr.

It will use the dir app/tmp/cache/flickr for caching

1. Copy phpFlickr to app/vendors/phpflickr/

2. Copy this to app/controllers/components/flickr.php and change the api key to yours (you can get your api key in https://sourceforge.net/project/showfiles.php?group_id=139987 )

<?php

/**
 * Flickr Component
 * @author RosSoft
 * @license MIT
 * @version 0.1
 */
define(’FLICKR_CACHE_DIR’,CACHE . ‘flickr/’);

class FlickrComponent extends Object
{
    /**
     * Api Key. Change to your own
     * @var string
     * @link http://www.flickr.com/services/api/misc.api_keys.html
     */
    var $_api_key=’CHANGE_TO_YOUR_KEY’;

    function startup(&$controller)
    {
        vendor(’phpflickr/phpFlickr’);
   
        //FlickrComponent instance of controller is replaced by a phpFlickr instance
         $controller->flickr =& new phpFlickr($this->_api_key);
         if (!is_dir(FLICKR_CACHE_DIR))
        {
            mkdir(FLICKR_CACHE_DIR,0777);
        }                
         $controller->flickr->enableCache(’fs’, FLICKR_CACHE_DIR);
         $controller->set(’flickr’,$controller->flickr);
    }
}
?>

Render XML with XSLT (XsltView) (updated) June 3, 2006

Posted by rossoft in CakePHP.
4 comments

Render XML with XSLT.
The xml can be a file in views/controller_name/action.xml (will be rendered like .thtml files, you can put php code in it)
or a view-variable (in the controller: $this->set(’xml’,$xml_string); )
Same for xsl, views/controller_name/action.xsl or $this->set(’xsl’,$xsl_string);

Examples: put those actions in your controller:
Example1:
//xsl through views/controller_name/action.xsl file, xml through view-variable

function example1()
{
$this->view='Xslt';
$xml='
<expenses>
<item date="2004-11-03">
<desc>Lavado completo</desc>
<location>Barcelona, Spain</location>
<amount currencyCode="EUR">50.45</amount>
</item>

<item date="2004-11-04">
<desc>Comida</desc>
<location>Londres, Inglaterra</location>
<amount currencyCode="GBP">35.2</amount>
</item>
</expenses>';
$this->set('xml',$xml);
}

Example2:
//xsl through views/controller_name/action.xsl file, xml through views/controller_name/action.xml file

function example2()
{
$this->view=’Xslt’;
}

Copy this to app/views/xslt.php:

<?php
/**
 * Xslt View
 * Renders XML with XSLT.
 *
 * The xml can be a file in views/controller_name/action.xml (will be rendered like .thtml files, you can put php code in it)
 * or a view-variable (in the controller: $this->set('xml',$xml_string); )
 * Same for xsl, views/controller_name/action.xsl or $this->set('xsl',$xsl_string);
 *
 * @author RosSoft
 * @version 0.11
 * @license MIT
 *
 * @link http://alexandre.alapetite.net/doc-alex/xslt-php4-php5/
 */
class XsltView extends View
{
    function render($action = null, $layout = null, $file = null)
    {
        header('Content-Type: text/xml');
        $this->autoLayout=false;
        $file=null;       

        if ($action==NULL)
        {
            $action=$this->action;
        }       

        if (!isset($this->_viewVars['xml']))
        {
            $file=$this->get_filename($action,'.xml');
            ob_start();
            parent::render($action,$layout,$file);
            $xml=ob_get_clean();                       
        }
        else
        {
            $xml=$this->_viewVars['xml'];
        }

        $this->hasRendered=false;
        if (!isset($this->_viewVars['xsl']))
        {       
            $file=$this->get_filename($action,'.xsl');
            ob_start();       
            parent::render($action,$layout,$file);
            $xsl=ob_get_clean();
        }                       
        else
        {
            $xsl=$this->_viewVars['xsl'];
        }
       
        $xh = xslt_create();
        $args=array('xml'=>$xml,'xsl'=>$xsl);
       
        $result=xslt_process($xh, 'arg:xml','arg:xsl',null,$args );
        echo $result;
    }
   
    /**
     * Returns the filename associated with the action
     * with the extension ".$ext"
     *
     * @param string $action If null, then current action
     * @param string $ext Extension of the view template (with dot) Example: '.xml'
     * @return string
     */
    function get_filename($action,$ext)
    {       
        $old_ext=$this->ext;
        $this->ext=$ext;
        $fn=$this->_getViewFileName($action);
        $this->ext=$old_ext;
        return $fn;
    }
}      

/**
 * PHP4-5 Compatibility
 * @link http://alexandre.alapetite.net/doc-alex/xslt-php4-php5/index.en.html
 */
if (version_compare(PHP_VERSION,'5','>=')&&extension_loaded('xsl'))
{
    /*
     Requires PHP5, uses included XSL extension (to be enabled).
     To be used in PHP4 scripts using XSLT extension.
     Allows PHP4/XSLT scripts to run on PHP5/XSL
   
     Typical use:
     {
      if (version_compare(PHP_VERSION,'5','>=')&&extension_loaded('xsl'))
       require_once('xslt-php4-to-php5.php');
     }
   
     Version 0.4, 2005-08-28, http://alexandre.alapetite.net/doc-alex/xslt-php4-php5/
   
     ——————————————————————
     Written by Alexandre Alapetite, http://alexandre.alapetite.net/cv/
   
    Copyright 2004-2005, Licence: Creative Commons "Attribution-ShareAlike 2.0 France" BY-SA (FR),
     http://creativecommons.org/licenses/by-sa/2.0/fr/
     http://alexandre.alapetite.net/divers/apropos/#by-sa
     - Attribution. You must give the original author credit
     - Share Alike. If you alter, transform, or build upon this work,
       you may distribute the resulting work only under a license identical to this one
     - The French law is authoritative
     - Any of these conditions can be waived if you get permission from Alexandre Alapetite
     - Please send to Alexandre Alapetite the modifications you make,
       in order to improve this file for the benefit of everybody
   
     If you want to distribute this code, please do it as a link to:
     http://alexandre.alapetite.net/doc-alex/xslt-php4-php5/
    */
   
    $xslArgs=null;
    function xslt_create() {return new xsltprocessor();}
    function xslt_free($xh) {unset($xh);}
    function xslt_errno($xh) {return 7;}
    function xslt_error($xh) {return '?';}
    function xslt_process($xh,$xmlcontainer,$xslcontainer,$resultcontainer=null,$arguments=array(),$parameters=array())
    {//See also: http://alexandre.alapetite.net/doc-alex/domxml-php4-php5/
     //Based on: http://www.php.net/manual/ref.xsl.php#45415
     $xml=new DOMDocument();
     $basedir=$xh->getParameter('sablotron','xslt_base_dir');
     if ($basedir && ($workdir=getcwd()))
      chdir($basedir);
     if (substr($xmlcontainer,0,4)=='arg:')
      $xml->loadXML($arguments[substr($xmlcontainer,4)]);
     else $xml->load($xmlcontainer);
     $xsl=new DOMDocument();
     if (substr($xslcontainer,0,4)=='arg:')
      $xsl_=&$arguments[substr($xslcontainer,4)];
     else $xsl_=file_get_contents($xslcontainer);
     $xsl->loadXML(str_replace('arg:/','arg://',$xsl_));
     $xh->importStyleSheet($xsl);
     global $xslArgs;
     $xslArgs=$arguments;
     foreach ($parameters as $param=>$value)
      $xh->setParameter('',$param,$value);
     $result=$xh->transformToXML($xml);
     if (isset($resultcontainer))
      file_put_contents($resultcontainer,$result);
     if ($basedir && $workdir)
      chdir($workdir);
     if (isset($resultcontainer))
      return true;
     else return $result;
    }
    function xslt_set_base($xh,$base) {$xh->setParameter('sablotron','xslt_base_dir',str_replace('file://','',$base));}
    function xslt_set_error_handler($xh,$handler) {};
   
    class xslt_arg_stream
    {
     var $position;
     var $xslArg;
     function stream_eof() {return $this->position>=strlen($this->xslArg);}
     function stream_open($path,$mode,$options,&$opened_path)
     {
      $this->position=0;
      $url=parse_url($path);
      $varname=$url['host'];
      global $xslArgs;
      if (isset($xslArgs['/'.$varname]))
       $this->xslArg=&$xslArgs['/'.$varname];
      elseif (isset($xslArgs[$varname]))
       $this->xslArg=&$xslArgs[$varname];
      else return false;
      return true;
     }
     function stream_read($count)
     {
      $ret=substr($this->xslArg,$this->position,$count);
      $this->position+=strlen($ret);
      return $ret;
     }
     function stream_tell() {return $this->position;}
     function url_stat() {return array();}
    }
   
    stream_wrapper_register('arg','xslt_arg_stream');
}

?>