La comunità italiana su CakePHP

You are not logged in. Please login or register.


Pages: 1

Atom RSS

Posts [ 3 ]

paamayim

Topic: query personalizzata e setup paginator

Salve a tutti, sono nuovo del forum e un po' anche di Cake. Sto lavorando su un software di terzi basato su una versione di 1.2 pre-beta di Cake.

Ho la necessità di impostare un browse di elementi con paginazione. Visto che questo elenco dovrà essere molto personalizzato e con varie associazioni, vorrei usare una query vecchio stile per avere (forse?) un maggiore controllo visto che per ora sono più ferrato sulle query vecchio stile che sui metodi di Cake.

Quello che vorrei fare quindi nel controller è poter scrivere:

$this->Item->query("SELECT ....... WHERE .... GROUP BY ... etc etc");

e poi configurare il $paginator da usare nella view, è possibile secondo voi? Senza passare da ->paginate() ?

Grazie.

arialdomartini

Re: query personalizzata e setup paginator

paamayim wrote:

Quello che vorrei fare quindi nel controller è poter scrivere:

$this->Item->query("SELECT ....... WHERE .... GROUP BY ... etc etc");

e poi configurare il $paginator da usare nella view, è possibile secondo voi? Senza passare da ->paginate() ?

Grazie.


No, direi che è arduo.
Se dai un occhio al metodo paginate() di Controller (/libs/controller/controller.php) ti accorgi che grosso modo esegue queste operazioni:

- estrae le opzioni passate nella URL (la pagina corrent, il limit, l'ordine di sorting)
- le aggiunge alla $conditions del metodo find() del Model
- *conta* i risultati

Un metodo generico che sia in grado di aggiungere alla tua generica query le condizioni LIMIT e ORDER BY sarebbe difficile da realizzare perché presupporrebbe un'analisi molto dettagliata della query: se ad esempio la query che fornisci avesse già un ORDER BY, ci sarebbe certamente il rischio di un errore SQL.

Mi sembra anche molto difficile, data una query generica, scrivere un metodo che restituisca il numero totale dei risultati, a meno di estrarli tutti e contarli (il che significa annullare quasi totalmente l'utilità della paginazione!).



Una possibile soluzione è questa:

crea un file app_controller sotto app/ con il contenuto:


<?php

class AppController extends Controller
{
   
    function customPaginate($query)
    {
        $model = $this->{$this->modelClass};
       
        $options = $this->params['named'];
       
        if(!empty($options['page']))
            $page = $options['page'];
        else
            $page = 1;
       
       
        if(!empty($options['sort']))
        {
            $query .= " ORDER BY {$options['sort']}";
            if(!empty($options['direction']))
                $query .= " {$options['direction']}";
        }
       
       
       
       
       
       
        if(empty($options['limit']))
            $options['limit']=$this->paginate['limit'];
       
        $limit = $options['limit'];

       
        $results = $model->query($query);
        $count = count($results);
       
        $frompage = $limit* ($page-1);
        $query .= " LIMIT {$frompage},{$limit}";
        $results = $model->query($query);
       
   

       
        $pageCount = intval(ceil($count / $limit));
        echo "Page count={$pageCount}";

        if ($page == 'last' || $page >= $pageCount) {
            $options['page'] = $page = $pageCount;
        } elseif (intval($page) < 1) {
            $options['page'] = $page = 1;
        }

       
            //$results = $object->find($type, array_merge($parameters,
$extra));
       
        $paging = array(
            'page'        => $page,
            'current'    => $count,
            'count'        => $count,
            'prevPage'    => ($page > 1),
            'nextPage'    => ($count > ($page * $limit)),
            'pageCount'    => $pageCount,
            'defaults'    => array_merge(array('limit' => 20, 'step' =>
1), $options),
            'options'    => $options
        );
        $this->params['paging']['Category'] = $paging;

       
        if (!in_array('Paginator', $this->helpers) &&
!array_key_exists('Paginator', $this->helpers)) {
            $this->helpers[] = 'Paginator';
        }
        return $results;
    }
   

}
?>


Dopo di che usi la paginazione come da te richiesto:

class UsersController extends AppController {

function index()
{
        $this->set('users', $this->customPaginate('LA TUA QUERY CUSTOM'));
}

}


Personalmente io NON lo userei nemmeno sotto tortura, per tre motivi:

1. E' un codice che ti ho scritto al volo, non l'ho mai utilizzato, è molto meno versatile dell'originale e probabilmente ha dei bug. Ha uno stile che definire ridicolo è poco. Andrebbe ottimizzato. E' poco performante.
2. Se sei costretto ad usare query custom, ripensa decisamente all'architettura della tua applicazione: l'ORM di Cake è decisamente più affidabile, comodo, versatile di un approccio più manuale
3. Ci sono dei limiti evidenti: quel codice si pianta se nella query è presente una clausola LIMIT o una ORDER BY.


Quindi, prendi quanto ti ho scritto solo come spunto.

Ma, davvero: perché mai dovresti usare delle query custom? Ti perdi il vantaggio delle relazioni, delle validazioni, della gestione delle condizioni con gli array.. tanto vale programmare in plain PHP ;)

Last edited by arialdomartini (28-10-2008 16:07:59)

paamayim

Re: query personalizzata e setup paginator

arialdomartini wrote:

Ma, davvero: perché mai dovresti usare delle query custom? Ti perdi il vantaggio delle relazioni, delle validazioni, della gestione delle condizioni con gli array.. tanto vale programmare in plain PHP wink

Hai perfettamente ragione smile

E' solo che ho da apportare modifiche ad un software che fa uso di CakePHP, quando io CakePHP non l'ho mai usato. Ho inziato a studiarmelo e fare prove, non è difficile, per carità, ma ci sono comunque cose che vanno sapute, cose noiose, che accadono in background e non te ne accorgi, le magie di Cake. E non ho molto tempo per questo.

Quindi come task avevo da aggiungere un "browser" di foto, con paginazione e conmolte opzioni (oridnamento per commenti, rating, per visite etc). La paginazione era già usata da altri controller ed era pronto anche un elemento paginator.ctp che potevo riusare al volo nella view, una volta settato $paginator a dovere, la query (più o meno) ce l'avevo pronta dalla versione vecchia del software in plain-PHP, non mi restava da joinare le due cose, query-old-style e paginazione cake-style.

Comunque morale della favola, è andata a finire bene, prendendo spunto qua e là, anche da parti di tuo codice. Alla fine è uscito qualcosa di abbastanza semplice e, secondo me, nemmeno tanto inutile, che ti vado a descrivere:

nel controller:

$perpage = 2; // test. 15, 20, ...
$start = ($page - 1) * $perpage;

// custom query!
$foos = $this->Foo->query("
    select sql_calc_found_rows
        Foo.*
    from foos Foo
    limit $start,$perpage
");

$this->set('foos', $foos);

nb: la query qua può essere complessa quanto ti pare, fare uso di join, group by, order, etc. con sql_calc_found_rows poi istruisce mysql a restituire il numero totale di elementi che non sono stati limitati da limit, che reprisce con una query sempre identica successivamente:

$count = $this->Foo->query("select found_rows() as count");
$count = $count[0][0]['count'];

Ora non mi restava che trasmettere al $paginator le informazioni di paginazione (numero pagina, numero pagine, elementi per pagina, etc, etc). E lì ho trovato la vera difficoltà, ma uno spunto è venuto furi anche dal tuo codice ed è quell'array $paging:

$npages = (int)ceil($count/$perpage);

$this->set('paging', array(
    'page'  => $page,
    'current' => count($foos),
    'count' => $count,
    'prevPage' => ($page > 1),
    'nextPage' => ($count > ($page * $perpage)),
    'pageCount'=> $npages,
    'defaults' => am(array('limit' => 20, 'step' => 1), .... ??? ),
    'options' => array($this->params['pass']) // per mantenere i parametri nell'url
));

Questo nel controller.

Nella view poi ti ho detto che avevo pronto l'elemento paginator.ctp, quindi ho fatto:

<?php $paginator->params['paging']['Foo'] = $paging; ?>
<?php echo $this->renderElement('paginator') ?>

Posts [ 3 ]

Guest posting is disabled. You must login or register to post a reply.

Pages: 1

Topic info

1 guests and 0 users are reading this topic now


Forum quick jump menu

Currently used extensions: pun_topic_online_users, pun_karma, pun_admin_hook_navigator, pun_bbcode. Copyright © 2008 PunBB

[ Generated in 0.027 seconds, 11 queries executed ]