A HolisticNetworking Information Resource
CakePHP Links » Home || Downloads || Docs

The Router::connect() pass array

I’ve discovered an interesting behavior in CakePHP that, as far as I know, is not documented in the book. I’d rather not attempt to edit the book – I’m not sure I’m right and anyway, they’ve just changed the book in some rather annoying way that I’d like to observe a bit before using – so instead I’ll just note it here for now.

The behavior in question concerns the pass array that exists in the Controller $this->params array and also the one that exists in the Router::connect function. Apparently, these two are connected in at least one very specific way. Namely: the order of elements in the Router::connect pass array overrides the order of elements in the URL model. Consider the following:


/rating_events/ny/east_rochester/12

/**
* Entry in routes.php:
*/
Router::connect(
   '/rating_events/:location/:city/:company',
   array('controller' =>; 'rating_events', 'action' =>; 'index'),
   array( 'pass' => array('company', 'location', 'city'),
      <-- Insert regular expressions here -->
   )
);

In the above code, if the pass array was not set,we would expect that the pass array in $this->params['pass'] would follow the natural order in the URL:

array('ny', 'east_rochester', 12);

But with the ‘pass’ array set in Router::connect, the array actually comes out like this:

array(12, 'ny', 'east_rochester');

You can therefore define the exact order of elements in that resulting params array. I can see this being handy as in the development process I’m currently going through, where the order and number of parameters passed in this way might be different from Controller to Controller, but always referring to much of the same data and using a lot of shared functions. Being able to organize the order of this array – for example, always having city and location appear in the same order and at the same numeric position – lowers the cost of processing and developing later when you don’t need to make exceptions in your code for Controller A versus Controller B.

I happened upon this when reorganizing my URL structure to try to make it universal. I couldn’t get the params['pass'] array to work, so I printed it out. Imagine my surprise when it was a completely different order than I expected!

No comments yet

Including Models in CakePHP Components

For reasons I’m not entirely clear on, CakePHP developers object in general to the practice of including Models into your Componets. I suspect the problem lies in the idea that you might end up with lots and lots of associated Models within the overall framework of a given Controller, leading to bad performance. That makes some sense, but why aren’t there any directions then on how to include the Controller’s Models into the Component, when its actually quite easy to do?

I dunno. But I finally figured it out and I’m passing along the information.

The trick is using the initialize() function. I presume that CakePHP must look at all included Objects to see if this function exists, otherwise I’m not entirely sure how else it becomes a globally recognized function. Perhaps someone more knowledgeable can chime in down there in the comments thingy?

Still, here is some sample code that first includes the Controller into the Component as a reference (taken from this example in the CakePHP Book) and then takes the logical next step of including the Model (in this case, WebPriceView) into the Component as well:

/*
// initialize:	Initializing the Component, importing some needed Objects from the Controller.
*/
public function initialize(&$controller) {
	$this->controller	=& $controller;
	$this->WebPriceView	=& $controller->WebPriceView;
}

It bears mentioning that this example can be extended well beyond including Models to including helpers and anything else you need from the Controller.

No comments yet

Some Hard-Learned Lessons on CakePHP ACLs

I’ve been bouncing my head roughly against the wall and trying to figure out a number of critical points about the way that CakePHP handles ACLs and permissions, authentication and authorization. Since discovering what is as close to a closed loop of information about the whole process, I’ve come to the conclusion that doing anything less than documenting it here would be just plain mean. As with all my posts, I do not pretend to be an expert in any of this, I am simply passing along observations as I find them:

CakePHP’s Concept of Basic Security is Essentially Wrong.

This was an unsettling one to come to, but there’s no other way around it. The Auth component is the component that handles allowing or disallowing access to things. Strictly speaking, since “Auth” means “Authentication,” according to the Cake Book, this is wrong. Authentication is the process of correctly identifying one’s self, which has nothing to do with whether or not the correctly-identified person has access to a given resource. “Authorization” is the process of granting and denying access  I realize that in many applications, simple authentication login/out type authorization works fine. But in the case of an application that uses both Auth and ACLs, Auth should properly surrender the authorization role and defer to the ACLs.

It does not. In fact, the ACLs don’t actually handle authorization at all, merely documenting the desired level of authorization. This is exactly opposite of the expected behavior. I realize now I will get comments telling me how very, very wrong I am about the whole business. But I don’t think I’ve forgotten that much about my Security+ certification.

Granting and Denying Access is Up to You

Right along with that, a quick explicit note: you will have to handle the granting and denying of access to actions/controllers/resources yourself with the Acl->check() function. If the user checks out, let ‘em through. If they don’t, bounce ‘em. Very explicit. I’ll get more into this further down the page.

Aliases are the Way of the (CakePHP ACL) World

Even though the alias field is an optional field for both ACOs and AROs, CakePHP still expects to be able to access the various elements through a sort of pseudo-alias, if such a term even makes sense. Instead of referring directly to the node’s ID – which lets face it, would get cumbersome – the ACL Component expects to refer to a combination of the model and foreign_key fields to identify it’s target node. I presume – read: have no first hand knowledge to the effect – that since the ACO table also includes the model and foreign_key fields, an ACO with no alias would also rely on this same pairing. Fortunately, while this is not documented really at all, once you know this it makes life a whole lot easier to simply grab user ID information out of the Session variables and go to town.

Weird and Significant: <node> and <node> are Not the Same Thing! (ta-da!!)

Did you run “cake acl help” only to find that both the grant() and deny() functions contain the ambiguous <node> parameters? Did you know which one is which? Me neither. The first parameter is the ARO node, whereas the second is the ACO node. Irritating vagueness in the help menu which if I can find the time, I will try to submit a patch for. ~ Update: I’ve submitted a patch to Lighthouse. They’re pretty quick assigning and updating patches, so assuming they agree with my assessment, it might even be updated in trunk by the time you read this!

There are Two Distinct Methods of Assigning Access

Those of us familiar with network security tend to think of ACLs in terms of limiting the CRUD access of users. CRUD stands for Create, Retrieve, Update, Delete. You can see these types of permissions in Windows Security properties of a file or folder, or in the standard chmod formulas most PHP developers know well, or in Mac file properties as well. It’s a fairly standard access schema.

In CakePHP’s ACL concept, each combination of ACO and ARO comes with it’s own standard set of these fields. Refer to the aco_aro table in your database for details. When you use the allow() and deny() functions, the last parameter refers to these fields. You can also provide additional fields to this database which can also be set where needed. A flag that allows access to admin routes on specific controllers, for example, would be handy.

The second method would be to assign ACOs to each public function in your controllers. You could explicitly list every action within every controller in your application. The CakePHP Book actually provides a nifty batching tool to help you do just that, albeit buried in a bunch of other stuff. This allows you much more explicit control of each function within your application.

Without an Explicit Reason, Using CRUD Fields is Better Than Using Actions

And if you really need to get right down in it – I mean, really specific to an exact function – this would be the way you would go about restricting access to a specific action. But generally, I find that most actions boil down to the basic CRUD concepts anyway.

That being the case, since CakePHP already provides you CRUD values for each association in the aros_acos table and as stated, a very explicit way of using them, why not just take advantage of what’s there? In the event that there is a specific function for which specific access needs to be granted – for example, one that does more than simply adding or deleting something from the database – then perhaps specifically calling out this action is required. The difficulty is that once you get down to these explicit actions, CakePHP’s Tree Behavior doesn’t really seem to work quite the way I’ve expected it to in the past. One would expect that, if the parent Controller is assigned a set of permissions and if the action is associated to the Controller as a child, the permissions would cascade down. Either I’m doing things wrong – entirely possible – or else it doesn’t really work that way.

Anyway, I hope this information proves clarifying or illuminating to some of you out there. If you have other questions, I can’t promise I’ll answer correctly, but please ask anyway in the comments. I’m sure corrections and modifications can be made along the way, so for those of you with greater knowledge than my own, I invite you to point out the flaws in the comments as well. Thanks!

No comments yet

Associating Models Without a primaryKey in Common

It happens from time to time that we need to associate Models which do not share a common key. An example of this is when I needed to associate pages with templates in a complex join where the PageSettings Model has a primary key of pageid and the PageTemplate Model has a primary key of template_id. We cannot use the primaryKey index of the association array to indicate the relationship, so we need to bypass that index altogether and instead use ‘conditions’ to specify the relationship as follows:

var $hasOne = array('PageTemplate'	=> array('foreignKey' => false,
								       'conditions'	=> array('PageSettings.page_template = PageTemplate.template_id')));

Note also that the ‘conditions’ clause is set using explicit conditions, inasmuch as we are stating that the PageSettings.page_template in the current query should be the same as the PageTemplate.template_id value. I’ve personally been confused in the past, thinking that the ‘PageSettings.page_template’ => ‘PageTemplate.template_id’ formula would naturally be the preferred way of forming the condition in CakePHP. Certainly, that would make sense based on convention, but for reasons I’m not familiar with, this is not how they do it.

No comments yet

Organizing Your CakePHP Files

I’ve seen many threads on the CakePHP Google Group about how to organize files on a CakePHP installation, but many of those threads are very old and I don’t think they’re always all that relevant. Once a project gets involved, or if the project is aimed at recreating an already-complex structure, it is common to have many files of similar function that it only makes sense you would want to organize into subfolders for expedience – not to mention neatness. Recently, I’ve had cause to start trying to organize files and thought I’d share a few observations. If you find any errors in my logic or ambiguity in the way I explain things, please comment below so I can make the corrections as needed.

Controllers and Models

Probably the most convenient in terms of CakePHP’s ability to flexibly handle subdirectories are Models and Controllers. Here you have the ability to just throw files into subdirectories of the /controllers or /models folders at will and CakePHP will automatically find them at run time with no extra configuration. I have not tested folder depth: I have not see whether /controllers/deep/path works as well as /controllers/path, but certainly a single subdirectory works fine, even with files that were already created in the root directory and later moved.

Views (and Elements, and Layouts, and so on..)

With Views and Elements, subdirectories still work, but in this case the subdirectory path must be specified. If you put an element file inside /views/elements/charts/template_1.ctp, then the path relative to the /elements folder must be specified like so:

<?php echo $this->element('charts/template_1', $args); ?>

So, that’s pretty much all I had to add to the conversation. I hope this relatively simple blog post helps a few of you out there looking for a straight-forward answer to the question, “How do I organize my CakePHP files?”

No comments yet

Models: Well, I’ll be Damned…

So, it turns out that you don’t really need a Model file in order to access database tables. I’ve done it with two different tables (quite accidentally) and it worked fine.

Mind you: I would not recommend such a thing. After all, if there is stuff you want to change about the way you handle a given table (from creating custom query functions to changing the name of the Model because of an unfortunately-named table, to using an entirely different database for a given table), you can’t do anything without the Model file. And you may as well make sure that the file works correctly from the jump rather than scratch your head over it later. But this is very interesting….

No comments yet

Interesting Form->input Behavior for Selects

Just a quick note to discuss an interesting set of behavior which, while they may seem obvious to some, struck me as a bit counter-intuitive when working on a form recently. I was helped in this endeavor by a WebbedIT in this thread of the Google CakePHP Group. But I took things a bit further out of curiosity and thought I’d share it here.

The form in question pulls data from a table of location whose primary key is the zip code. The fields are zipcode, city and state. I intended to create a form that would allow a person to select either the city/state combination or the zip code. Obviously for the sake of usability, I wanted to make the state choices a select rather than a standard input.

But of course, CakePHP’s automagic selects are built from the find(‘list’) method and that method wants to use the primary key of the table as the value of the select by default. Hence I ended up with the following:

// In the Controller:
function beforeRender() {
	$states = $this->Location->find('list', array('fields' => array('Location.state'), 'group' => array('Location.state')));
	$this->set('states', $states);
}

// In the View:
<?php echo $form->input('state'); ?>

// Output to browser: ?>
<select id="LocationState" name="data[Location][state]">
    <option value="98791">AK</option>
    <option value="35004">AL</option>
    <option value="71601">AR</option>
    <option value="85003">AZ</option>
    <option value="90001">CA</option>
    ...
</select><?php

That is definitely not what I intended at all. As WebbedIT pointed out, the solution was to include two fields in the request, both the same actual state field as follows:

$states = $this->Location->find('list', array('fields' => array('Location.state', 'Location.state'), 'group' => array('Location.state')));

Well, OK. That gets me past my problem, but what’s this all about? What happens if I select two different fields? What if I select three? What happens when I change the order of fields?

The answer after copious screwing around is that it seems CakePHP organizes the the results in the following method: [optgroup], value, display. I have really no idea what would happen if we extended beyond three fields and honestly don’t have the time to find out. For example, if we grabbed fields in the following call:

$states = $this->Location->find('list', array('fields' => array('Location.city', 'Location.state'), 'group' => array('Location.state')));

We can expect that the city will be used as the displayed text and the state would be used as the value of the option. If we grabbed three fields from the table, we would end up with option groups in the resulting select element:

// This query:
$states = $this->Location->find('list', array('fields' => array('Location.city', 'Location.state', 'Location.zipcode'), 'group' => array('Location.state')));
// Gives us:
<select id="LocationState" name="data[Location][state]">
    <optgroup label="98791">
        <option value="98791">AK</option>
    </optgroup>
    <optgroup label="35004">
        <option value="Acmar">AL</option>
    </optgroup>
    <optgroup label="71601">
        <option value="North Cedar">AR</option>
    </optgroup>
    <optgroup label="85003">
        <option value="Phoenix">AZ</option>
    </optgroup>
    <optgroup label="90001">
        <option value="Los Angeles">CA</option>
    </optgroup>
    ...
</select>

I can see lots of reasons why this option group thing would be good to have around, though in my case because I’m grouping, it makes less sense. If I ungrouped the items, I’d get a city presumably in every zip code.

I hope this helps some people figure out what is going on with CakePHP’s automagic selects. The functionality is definitely helpful but not at all documented that I’ve seen and definitely counter-intuitive.

No comments yet

Phase Two Development: Order Matters!

Another post in my Development Walk-Through Series, following me through the development of my own application to share my thoughts and observations. Please consider subscribing to my feed if you find this information helpful.

Now that you’re working on your Models in Phase Two of your development process, you have a second opportunity to trim up your database. Once you start putting together CakePHP queries, it quickly becomes obvious that CakePHP will arrange your queries according to it’s own sense of MySQL rather than what you might have originally thought. This means that the database development you did in Phase One will have to be altered a bit.

But the way you put together queries is also important. For example, when assembling the ['conditions'] array within your query, the order in which you specify the conditions is directly reflected in the way the conditions are built into the query.

Why is this important? Well for a start, because if you’re using any multi-column indexes, those indexes rely on the order of columns in the query being in the same order as they appear in the index. Without the correct order, they simply cannot be used. I don’t have any proof to back this up, but it stands to reason that this fact may point the way to another tweaking possibility: that the order of columns probably matters regardless of whether or not you use multi-column indexes.

The take away from this is that when you create queries in the CakePHP manner, it’s important to look at the resultant query and make sure it’s behaving the way you expected it to. If you can tweak the query, do it. If you cannot, it’s probably time to rethink some of your optimization strategy to better reflect the reality of the system in which you are working.

No comments yet

Testing Your Models: Phase Two Development

If you’ve been reading through my Development Walk-Through Series, this is Part II. Part I can be found here.

I’ve spent long hours tweaking my database, reading up on relatively arcane MySQL tuning techniques, filling the database with dummy values, querying, checking, requerying. I’m reasonably certain that the database structure I’ve chosen is the best option for my application. At least, that’s the way it appears for now: databases always change over time, so it’s worth keeping a loose hold on your concept of “acceptable.” Now it’s time to start putting together the CakePHP side of the show.

And what’s the first step? Well, once you’ve uploaded your files and established a connection to the database, it’s time to start building the models1. Models are the CakePHP expression of your database structure and relationships. By properly building and configuring your database model files, you can speed the development of new database queries by allowing CakePHP to do a lot of the lexical heavy lifting. Instead of spending time getting frustrated trying to figure out what’s wrong with this code:

SELECT ratings.rating ratings.category
FROM `ratings_entries`
JOIN `ratings` ON ratings.rating_id = ratings_entries.rating_id
WHERE ratings_entries.cmp_id = %cmp%
	AND ratings_entries.zipcode = %zip%
	AND ratings.cat_id = %cat%"

You can simply create the relationship in rating.php, add the WHERE conditions to an array of variables and let CakePHP build the query for you2.

But there is an inherent difficulty and almost contradiction to the way CakePHP’s documentation explains building your first app. Namely, the CakePHP docs show you how to build a model, then a controller and finally three separate view files for each individual database table. This is a simple setup aimed at producing a working model, not an optimal example. For a start, you may not necessarily need to use controllers or views for every single database table. Beyond that, it is not at all practical to have to create a model, controller and view for every single table just to find out if the models you’re building are working or not. The contradiction is that, while models, controllers and views should be independent, the example forces them into an uncomfortable marriage.

The solution for your model testing needs is to create one controller and use the $uses variable to its best advantage, including all the models you need into a single location. This may be – and almost certainly will be – entirely too many models for any one practical controller in your application. But this is a short-cut method aimed at bringing in all your data models and working with them. Whereas the example application shows you the following basic configuration:

var $uses = array('Ratings');

You can instead load all your models into this single controller:

var $uses = array('Ratings','RatingEvents','KitchenSink');

Now you can test your basic queries and make sure they respond the way you expect. You can have a quick check for lexical errors in your PHP without having to build three other PHP files before you get started. In short, you can develop just the models and leave the controller and view development where they belong: in the controller and view development steps.

  1. The process for uploading and setting basic configuration of a CakePHP installation is well-documented and we shan’t go into detail here.
  2. By the way, the problem with the example MySQL is that there is a missing comma (“,”) between ratings.rating and ratings.category

No comments yet

Calculation with PHP or MySQL?

I must stress at the outset that this post is merely my thinking out loud about a subject that has been on my mind with my new CakePHP project for some time. I do not claim any particular prowess with MySQL especially, but have discovered something rather interesting while trying to tune up my database.

I have made the point in posts past that building a new application with CakePHP is all about creating a proper database. Well, over the course of months (since, having a full-time job, I don’t get to work on my project as much as I’d like), I’ve been developing what I’d hoped would be a well-normalized and efficient database. However, the question remains unanswered until you’re able to build a test fixture and start benchmarking your queries. In my case, since the new project is in fact a ratings system, the application relies heavily on performing averages.

Thus one additional question is: does calculating in MySQL perform better or worse than calculating in PHP? It stands to reason that there might be some performance variance between the two, just because they’re two different things and therefore one might assume that they might approach the same relatively simple task in different ways. However, using my test fixture, I’ve found that at least in the relatively simple case of doing averages, there’s really no performance difference between the two.

The test fixture I created selects a random but still predictably-valid set of criteria for a given query and performs the query one hundred times in a row. In the case of the MySQL calculations, it then simply returns the microtime difference between the start and end of the query each time (to create a stopwatch effect). In the case of the PHP calculation routine, it performs the averaging calculation and then returns the same stopwatch value. In each case, the stop watch times are then averaged after the loop is finished to arrive at the final average calculation time. This test is then performed ten times in a row to account for variations between the various random samplings. Pseudocode is below:

for 10 iterations:
    for 100 iterations:
        get microtime1
        query database
        (if php) average values
        get microtime2
        stopwatch[] = microtime2-microtime1
    end iteration
    average stopwatch values
    return stopwatch average
end iteration

It should be noted that the database I’m using is very simple and holds a relatively trivial amount of data. I filled it with over half a million records among 7 tables, but the lion’s share are in the tables that require calculation, which is just two tables. The database after all that data still only holds about 19Mb of data. Its also worth noting that the table on which I’m performing calculations holds only an ID, a category ID and the calculated data. This was by design because of MySQL performance tips provided in this well-viewed video by MySQL guru Jay Pipes.

I may find that other configurations, such as adding or removing indexes, may yield different results. I’ll be posting my findings as I go, since that’s what this blog is all about.

No comments yet

Bad Behavior has blocked 7 access attempts in the last 7 days.