Leaning on CakePHP

There is a ton of information that CakePHP sets up for each request. Looking at the Controller object API, you can see that a sizable chunk is devoted to maintaining the data about the current request (as it should be) but what, if anything, are you doing with that information? Here's some tidbits I've ran into while developing a plugin that you can use to start "leaning on CakePHP" in your application.

One of the first things you should do for any application is to create your basic CRUD and index actions in the AppController. This give us a reliable base to build upon, helps keep things DRY and make our lives easier down the road by easily transitioning into API verbs. Here's an example of the index method...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

<?php/** * Handles index requests. * * @return void * @access public */public function index() {    // Only do this work if our concrete controller has not.    if (!isset($this->viewVars[Inflector::pluralize($this->modelClass)])) {        $this->set(array(            Inflector::pluralize($this->modelClass) => $this->paginate(                $this->modelClass,                /** * Here we toss in any conditions that were found as named * parameters and which match up to a column in the model * schema. */                array_intersect_key(                    $this->params['named'],                    $this->{$this->modelClass}->schema()                )            )        ));    }}

As this is intended to go into the AppController you can see that we're using $this->modelClass to determine the controller's default model. We also don't perform any "work" if we see that it has already been done by the controller. Finally, as a bit of nice to have code we are looking in the named parameters for anything that matches a column name and automatically applying that as a condition to our pagination. This lets us easily create an address such as /posts/index/author_id:1
One thing to point out here is that this can easily be overridden by the controller. If it needs to do more than is available we can easily create an index method in the "concrete" controller and only call parent::index() if it's still usable. Let's check out another method...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

<?php/** * Handles deletion requests. * * @param $id string * @return void * @access public */public function delete($id) {    if ($this->{$this->modelClass}->delete($id)) {        $this->_flashSuccess('Record successfully deleted.');        $this->redirect(array(            'action' => 'index'        ));    } else {        $this->_flashError('There was an error deleting the record.');    }}

[There are some custom methods defined in there that should be relatively self-explanatory] This one is pretty straight forward: The user asked us to delete something, we're going to attempt to delete it and based on the outcome give the user a textual response and possibly redirect them to the index action of the current controller.
I can already hear you saying "But... but... Joe, what if they don't have permissions to delete the record?" well, that's not the job of this method to determine. These are only methods to be built upon. Hence why they're in our AppController. Perform your authentication checks in the concrete controller and only call the parent method if everything checks out.
Alright one more now, my favorite.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

<?php/** * Handles update requests. * * @param $id string * @return void * @access public */public function update($id) {    if (!empty($this->data)) {        /** * Since the `create` method does most of the dirty work for us * we simply set away the ID for the records we're attempting * to update and fire off our request. */        $this->data[$this->modelClass][$this->{$this->modelClass}->primaryKey] = $id;        $this->create();    } else {        /** * By using our `view` method we'll get all of our view variables * setup for us. */        $this->view($id);    }}

[This code is extremely verbose because of the plugin's nature] Here we're leveraging on our own code. If the user hasn't sent any data we trigger our view method to get all of our view variables setup (and possibly perform authentication) before rendering. If the user has sent us data we simply toss in the ID of the record they're updating and then trigger our create method. How easy was that?
With this base of simple, straightforward CRUD methods you can save a large amount of time by reusing code and only writing modifications for specific cases.

Permalink

| Leave a comment  »