RSS
 

Posts Tagged ‘cakephp’

CakePHP, draggable/droppable in jQuery

27 Mar

Working on bunch of CRUD routines in CakePHP, I finally got tired of multiple checkboxes and decided to write a small helper for jQuery UI drag-n-drop functionality.

In response to Teknoid post, I’ve made couple of additional tweaks to dynamic select boxes, and got quite nice combination of selectboxes and dynamic lists.

The main requirement of the script was:

  1. On Selectbox value changed, return the list of related items (in #base-list div-element)
  2. Add drag/drop functionality to the base-list ul elements as well as add arrow-west/arrow-east addition/deletion of the elements
  3. After all the movements are over, to be able to save dragged values in CakePHP form to be saved.
  4. Result: here.
  5. Sample MySQL DB file: Here

The view/helpers/droppable.php looks like:

 tag
    */
    function droppableElements($outside = false) {
        $js_code = "";
 
        $js_code = '
                    var $baseDiv = $(\'#base-list\');
                    var $targetDiv = $(\'#target-list\');
 
                    $(\'li\', \'#baseUl\' ).draggable({
                        accept: \'#baseUl > li\',
                        revert: \'invalid\',
                        helper: \'clone\',
                        cursor: \'move\'
                    }); 
 
                    $(\'li\', \'#targetUl\' ).draggable({
                        accept: \'#targetUl > li\',
                        revert: \'invalid\',
                        helper: \'clone\',
                        cursor: \'move\'
                    }); 
 
                    $targetDiv.droppable({
                        accept: \'#baseUl > li\',
                        activeClass: \'ui-state-highlight\',
                        drop: function(ev, ui) {
                            moveItem(ui.draggable, $targetDiv);
                        }
                    });
 
                    $baseDiv.droppable({
                        accept: \'#targetUl > li\',
                        activeClass: \'ui-state-highlight\',
                        drop: function(ev,ui) {
                            moveItem(ui.draggable, $baseDiv);
                        }
                    });
 
                    $(\'#baseUl > li\').click(function(ev){
                        moveItemClicked($(this), $(this).parent());
                    });
 
                    $(\'#targetUl > li\').click(function(ev){
                        moveItemClicked($(this), $(this).parent());
                    });';
 
            if($outside) {
                $this->out .= $this->Javascript->codeBlock($js_code);
                return $this->out;
            } else {
                return $js_code;
            }
 
    }
 
    /*
    *   @var string $boxId - observed selectBox id element
    *   @var string $eventType - default 'change'
    *   @var string $jsonAction - method call, i.e '/departments/ajax_get_list/departments/company_id/'
    */
 
    function observeSelectBox($boxId, $eventType = 'change', $jsonAction) {
        $js_script = "";
 
        $js_script ='
          $(\'#'.$boxId.'\').live(\''.$eventType.'\', function(){
            if($(this).val().length != 0) {
            $.getJSON(\''.$jsonAction.'\',
                {searchParam: $(this).val() }, 
 
                function(items) {
                    $(\'.jq_list\').remove();
                    generateBaseList(items);
 
                '.$this->droppableElements().'
                }
            );
        }
        } );';
 
        $this->out .= $this->Javascript->codeBlock($js_script);
 
        return $this->out;
    }
 
}?>

For using so-called ajax_get_list method, I’ve written a small function in app/app_controller.php that get all the id,name list array based on couple of arguments:

In order to use this method, you should have a small layout in related views folder, that looks like:

<?php
    // somewhere in views/departments/ajax_get_list.ctp
   if(isset($items)) {
        echo $javascript->object($items);
 }?>

The view of ajax_get_list method will output serialized array of the method. For the initial view, you need to add 2 div-elements and one select box. Div elements have static id’s which are used by jQuery script to look-up (base-list, and target-list).
In our view, it should look like:

<?php  echo $droppable->observeSelectBox('company-id','change','/departments/ajax_get_list/departments/company_id/');?>
 
        <label> Load company departments: </label>
        <?php print $form->select('Company.id', $companies, null, array('id'=>'company-id'), 'Company Departments);?>
 
        <?php print $html->div('right block', $html->div('target-title', 'Container', array(), false),array('id'=>'target-list'),false);?> 
 
        <?php print $html->div(null,null, array('id'=>'base-list'));
?>

And that’s about it, once you finish dragging the li-elements to the target-list, and save the form, its values will appear in ['Container']['item_ids'] array where you can do whatever you want with it.
To make things simple, you can download a small sample of code of these files here, as well as database sample file.

 

CakePHP: remove tmp folder from svn control

01 Mar

We’d have to use propset command from SVN.

$#: cd /path/to/repository/cake_project
$#: svn delete app/tmp/*
$#: svn propset svn:ignore '*' tmp/

Now, after you commit the changes your tmp/files won’t be commited to the SVN repo.

 

Cakephp 1.2.x: Distinct Select

24 Sep

Just a reminder for myself:

$foo = $this->$model->find('all', array('fields'=>array("DISTINCT {$model}.dnid")));

CakePHP 1.1.x style used to be:

$this->$model->findAll(null, "DISTINCT {$model}.city");
 

CakePHP: jQuery styling of flash messages

30 Jan

CakePHP session flash messages is one of the most useful things for usability, and user notification. Trying to make it more fancy, I came to jQuery framework with its HighlightFade plugin.

Adding just a couple of lines, we can get nice effects, looking the same as WordPress messages in Posting/Editing:

      var $j = jQuery.noConflict(); //I'm using Prototype as well, so we don't need conflicts
$j(document).ready(function() {
       if( $j("#flashMessage") ){ 
          $j("#flashMessage").highlightFade({color:'#24F273',speed:2000, iterator:'exponential'});
       }
});

Thus, next time you will pass $this->Session->setFlash('Foo');, your flash message with default

<div id="flashMessage"><?php $session->flash();?></div>

will be used with above HighLight effect.

 

CakePHP: named over url parameters passing

05 Jan

Playing around with CakePHP parameters passing through URL, I’ve noticed that using Paginator helper for listing entities mixes up named and url params in the URL.

For instance, classical paginator URL is:

http://domain.com/controller/action/page:number


which looks like:

Array(
[named] =>Array(
[page] => number
)
)

Any parameter used in the Paginator goes inside of the URL and doesn’t affect the behaviour (in my case, URL param is used for language switching), i.e. http://domain.com/controller/action/?param=foo/page:number
No matter what your $this->params['url']['param'] contains – it won’t reflect on the logic.
Thus, if we use another variable $this->params['named']['foo'], we can always access it in our viewers and utilize it in the $paginator variable, so our URL will look like:
http://domain.com/controller/action/page:number/foo:bar
Useful links:
Additional parameters in $paginator

 

CakePHP: complex SELECT queries

03 Sep

As the project grows I had to work on some more complex queries to provide users with better searching facilities.

In this case, you might use two options:

  • Straightforward find() function from App::Model (where you’ll have to handle the outpu of data yourself, and trying to fit the search results in your websites layout)
  • Use Pagination functionality (which is designed for handling big chunks of data for you)

A simple example: from a small search menu, I need to get the data about item’s price, its type etc, so at this point, find() solution would look like:

$condition = array('OR' => array(
                   'Item.type'   => $this->data['Item']['type'],
                   'Item.qty'     => $this->data['Item']['qty'],
                              ),
  array('Item.price BETWEEN ? AND ?' => array($start_price,$end_price))
);
$this->('results', $this->Item->find('all', $condition));

Once you set results array in the view template, it will cause you few hours on how to rearrange data presentation, meanwhile you can use Pagination:

/*
* I'm going to use the same $condition
*  the difference will be at the view level and the way of setting the data "results"
*/
$this->set('results', $this->paginate('Item', $condition);

And in the view you might add some code like:

<?
echo $paginator->counter(array(
'format' => __('%page% of %pages%, showing %current%
records out of %count% total, starting on record %start%, ending on %end%', true)
)); 
 
foreach($results as $i => $item):
/*
* Items output
*/
endforeach;
?>
<?php echo $paginator->prev('<< '.__('Previous Page', true), 
array(), null, array('class'=>'disabled'));?>
 |     <?php echo $paginator->numbers();?>
    <?php echo $paginator->next(__('Next Page', true).' >>',
 array(), null, array('class'=>'disabled'));?>

Last lines of the code would manage the results listing for you, which has to be defined in your Controller:

var $paginate = array(
                         'Item' => array(
                           'limit' => 5,
                           'order'=> array('Item.added' => 'ASC')
                           )
            )

Done, now you can easily handle your search outputs. ;)

 

CakePHP: lists, beautiful bits

18 Aug

These tiny bits of beauty really make the development enjoyable:


$names = $this->find('list', array(
'conditions'=> null,
'order' => 'Developer.id ASC',
'fields' => array('Developer.id','Developer.'.$name.''),
'recursive' => 0
));

as the result, getting:

Array = (
[1] => 'Pafilia',
[2] => 'Vashiotis'
);

I’ll miss these features if I move from this framework :)

 

CakePHP: Dynamic select boxes with AJAX

07 Aug

Working with CakePHP1.2 I had to implement some AJAX techniques in the framework, mainly dealing with onChange behavior of the form components, here is a small example of how we can use multiple selectboxes.

Note: For simplicity reasons, I didn’t include queries, and just used option-arrays:

//inside controller
<?php
       class FooController extends AppController{
                var $name = 'Fooes';
                var $components = array('RequestHandler');
                var $helpers = array('Html','Form','Javascript','Ajax');
 
              function beforeRender(){
                          if($this->RequestHandler->isAjax()){
                                   Configure::write('debug',0);       
                                  //prevent useless warnings for Ajax
 
                          }
                          $this->set('foobar',null); 
                         // initiate an array of options (otherwise, you'll get a warning)
 
             }
             function updateDistricts(){
                                   $this->layout = 'ajax';
                                   $this->beforeRender();
                                   $this->render('updateDistricts/','ajax');
            }
 
      }
?>

This is just a simple layout for calling AJAX methods, without database, sofisticated layout handling, routes etc (for these reasons we got CakePHP API, and CakePHP GoogleGroup)

//inside viewer (test-data): <em>foo/update_districts.ctp</em>
<option>Limassol</option>
<option>Nicosia</option>
<option>Larnaca</option>
<option>Famagusta</option>

The actual form would look like:

//this code was used as an element from /view/elements/*.ctp
 
<ul id="navlinks">
                 <li><?php echo $form->create('foo');?></li>
                 <li>
                         <?php
         print $form->input('Districts.name', 
                                     array('type'=>'select',
                                      'options'=>array('Limassol','Nicosia','Larnaka'),
                                       'id'=>'district_name',
                                       'empty'=>'Choose District',
                                       'label'=>'District'));
       print '<span class="ajax_update" id="ajax_indicator" tyle="display:none;">'.$html->image('ajax-loader.gif').'</span>';
            ?>
                 </li>
                 <li>
                   <?php
       print $form->input('Towns.name',
                                   array('type'=>'select',
                                   'options'=> $foobar,
                                   'style'=>'display:none',
                                    'showEmpty'=>true,
                                  'empty'=>'City',
                                   'id'=>'city_name'));
 
        print $ajax->observeField('district_name', 
                                   array(  'url'=>'updateTowns/',
                                   'update'=>'city_name', 
                                  'loading'=>"Element.show('city_name');           
                                  Element.show('ajax_indicator')",
                                  'complete'=>"Element.hide('ajax_indicator');
                                 Effect.Appear('city_name')",
                                 'onChange'=>true));  
         ?>                                         
         </li>           
         <li><?php echo $form->end('Search');?></li>
</ul>

As the result, once you change the default value of the first select box, the second one automatically calls the controller’s method, which renders the values (gets the option list from the view file) and renders it in AJAX manner

.

Related article: CakePHP Droppable helper with Dynamic select box