debuggable

 
Contact Us
 
51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59

Basic CakePHP templating skills

Posted on 11/10/06 by Felix Geisendörfer

One of the things I don't see getting to much coverage is how to create good templates when working with CakePHP. Since those are written in plain PHP, this does not apply to CakePHP only. So I'm sure many people have already developed their own style that they are comfortable with and I don't ask for them to change it. However, maybe some people new to the framework / language can benifit by taking a look at the one I'm using.

PHP tags

The first thing I recommend is using the fully qualified syntax (<?php ?>) for php tags instead of the short one (<? ?>). Why? Well that's what the CakePHP coding standards recommend you to use, and it makes those statments stand out a little more in your html code. But the main reason still is that the short tags syntax can be turned off via php.ini, so using them makes your app less portable and could cause raw code output incidents.

Conditions

While you are used to use to wrap conditional commands in curly braces in your normal PHP code, they are not quite as practical in templates. Instead you should make use of this syntax which makes your code easier to read & write:

<p>Could not find a user matching your criteria</p>
<?php else: ?>
<p>The name of the User you are looking for is: <?php echo $user['User']['name']; ?></p>

The advantage of this is that you don't have to use curly braces to wrap your command blocks, but instead simply use if, else and endif to do so.

Loops

When working with CakePHP the foreach loop is your best friend. A lot of things are done via associative arrays and even plain numerical arrays are quite comfortable to process this way. The syntax I recommend is the same one as for if statements:

<h2><?php echo $html->link($event['Event']['title'], Event::getUrl($event)) ?></h2>
<?php echo $event['Event']['html'] ?>

The linebreak issue

Now when using loops (or several inline statements) and manually viewing your html output, you'll often notice that line breaks are apparantly swallowed, and everything is on one line. Well this is not CakePHP's fault, but rather the default PHP behavior. Whenever an inline statement ends with ?> followed by a line break, this line break will not appear in the output. But don't you worry, there is a simple fix for this: Simply add a space in between ?> and the line break and voila, the output turns out as expected.

Avoid multi-line statements

One thing I found usefull when writing templates was to adobt an "avoid multi-line commands" policy. This means that you should do one command per inline statement, and not several ones at once. On occassions I break this rule, but most of the time it serves as a very good indicator for showing you that you using Controller logic in your views, or you should start writing a Helper for something. The worst thing you can do, is define functions inside your View. Not only should those be moved to helpers, but when this view get's rendered twice in one request you'll get a nasty php error, telling you you can't define the function twice ...

Creating Zebra striped table rows

This is a little specific, but I've seen some really horrible approaches for assigning varying css classes to every row in the past, so this is how I would recommend you to do it:

<tr class="<?php echo ($num % 2) ? 'row-a': 'row-b'; ?>">
  <td><?php echo $event['Event']['title']; ?></td>
  ... More columns go here ...
</tr>

Since only every 2nd number in a consecetuive series of integer values can be divided by 2 without leaving a remainder (even numbers), this can be used to constantly switch the css class between 'row-a' and 'row-b'.

Alright, I hope this little tutorial will help some new folks, and maybe some others as well.

--Felix Geisendörfer aka the_undefined

 

What happens if you don't update your Blog regulary!

Posted on 6/10/06 by Felix Geisendörfer

Not too long ago, I made a promise to you as my readers. I promised that I would post one useful tip regarding the excellent CakePHP Framework every day for a period of 10 days. I also said what I would do if I would miss one day of posting:

In case I break my promise and miss only 1 day of posting a new tip (my time zone is GMT+1), I’ll take & post a picture of myself putting my face into a cake.

After already doing 14 posts (checkout the list on PHPDeveloper.org) in 8 days, I missed to do a post on day 9. It was Friday, I was tired and the next thing I remembered was waking up on my couch, knowing that I had missed to post a tip. So I admited, that I had lost my bet, and now it was time to take the beating.

Let's start with a picture of the Cake itself. It was a cheap frozen one that I picked up from a bigger store in the city. So far nothing special. But while I was preparing everything and the Cake was already sitting on my desk, he was smiling at me! Now you wouldn't believe that, but fortunately I was able to capture a shot of him doing that right before he turned back into a normal Cake, pretending it had never happened:

The evil Cake

Now even so my Cake seemed to have a great time, I didn't feel exited about this entire proccess at all.

Felix isn't exited

But well, I had to do it, and this time it was the Cake that didn't seem to have any fun. In case you have moral or religious concerns about violence against Cake's, you should probably skip this image.

The smashed Cake

And here comes my final punishment - getting the Cake all over my face. For those of you very interested in details: Yes I know there is one piece of Cake on my upper lip, and yes I am German, but NO this did not happen on purpose - I am no Cake nazi, I swear.

The eagle has landed

After the initial shock, I got angry at the Cake that had put me into this entire situation:

Getting angry at the Cake

And finally another shot of the blogger that didn't update his blog regulary. This one is looking rather awkward, but so what:

Resignation

So, I did it. I embaressed myself in the open public, and nobody is probably ever going to hire me again. But at least I've not broken my promise and shown the blogging community what can happen if you don't update your blog regulary. May other's be more lucky then I was ...

Please don't forget to digg this story, thanks!

--Felix Geisendörfer aka the_undefined

 

The Ignorant Client vs. The Passionate Developer

Posted on 4/10/06 by Felix Geisendörfer

As some of you might have suspected for years,- our fine field of web development is a place of war. The alliance of passionate web developers who care about standards, good code, accessibility, usability and other things are in a constant hate/love relationships with the union of clients who know as much about this field as some presidents know about 'nucular' weapons. The battlefield is filled with table layouts, WYSIWYG editors, inaccessible web 2.0 sites and bad code.

I think one of the biggest issues thesese days is, that we, the passionate developers, try to create systems a 10 year old could use. We fall for the clients demanding Word-like WYSIWYG editors and build right management systems for people we wouldn't trust to spell out the word 'Security'. And to make it even worse, the people we work for consider our work to be something everybody could do. The 90s created a mindset saying that everybody could make a web site (see this article). And it's true, every 12 year old can make his own web page. So I can understand the difficulty clients have when they are demanded to pay somebody $50 (or more) per hour for something their kids could do. I mean, how should one go about explaining them the difference between invalid tag soup and standard compliant beauty? How do you raise their aweareness for quality that's invisible to them?

I think the answer is education. Uhm, that doesn't sound very original, does it? So let me try to rephrase it. Taking away their WYSIWYG toys and replacing them with our geeky alternatives like Textile is not going to cut it. Telling them it's going to be very benifital for them isn't going to cut it either, because after looking at the obscure markup they already decided it's way to complicated and stoped to listening to you. I've tried it - saying "it's really simple, just take a look at it" - and failed. Same goes for other things like standards based html, accessible JS and good php code. Clients will not be able to percieve the difference between a good and a bad site just because you tell them it's bad to use table's, inline JS or even worse crimes against progress in our field. I think we've all tried to convince a client to use a certain technology or follow our suggestions and failed misserably because we bored them to death by being to technical. We spent way too much time talking in our geeky language with our geeky friends and build a huge barrier in terms of communication. And I think our only way out, is to understand how our clients think:

If I was a client, I would think it's perfectly fine to expect somebody to make a web page, for $300. I would tell the developer that I want this really sharp looking site and the ability to maintain it myself. I know my 13 year old son has his own site, so this should be more then enough money for the task, so maybe I can get it for as low as $250. So after sending out a short email with my basic requirements to 2-3 developers, they give me estimates ranging from $350 to $1500. The cheapest one says he'll use some system called Joomla and editing my page will be as easy as editing a word document. The most expensive one talks about (x)HTML, CSS, PHP, MySql, Textile, ACL, AJAX, Accessibility, Usability and how all of this will be really good in terms of SEO and standard compliance. Hm the expensive guy sure sounds like he knows a lot of stuff, but I really can't see how this beats "editing my page like a word document" for $350. So I give the job to the cheap guy, get an alright-looking web site, fill in my content and finally got rid of this "get a business web site" task from my todo list.

We as the passionate developers will look at the resulting web site with disgust. The cheap guy used Dreamweaver and created a table based layout, and if that wasn't bad enough, the WYSIWYG editor added an ugly tag soup called 'content' to the page. Search engines might have indexed the site, but the non existing alexa rank indicates the amount of traffic the site recieves. But I say that's the better of two scenario. The client got what he paid for. The other scenario is that you dropped your price and talked him into hiring you for $500-600. But now he expects magical things to happen in terms of design, while you are enjoying the frustration of tweaking CSS to work in IE and getting the WYSIWYG (he rejected to use Textile) editor not ruin your efforts and to spit out valid html. All of this while being payed ~$20 per hour. This is no fun and at some point you'll just want the project to be over. You'll fix some php bugs without knowing what caused them, deliver the site and take your money. You might have invested a lot more work, but the site won't get more visitors, nor will the client feel like he recieved a better page. Why? Because your passion turned into hate for the ignorance of the client. Because you feel like you were paid badly and the client was demanding too much.

Now it wasn't until recently that I got to enjoy working for media agencies instead of end consumers, that paid me a decent wage doing exactly what I'm good at - php coding. They were happy with the quality they got and exited about the passion I brought into the project. On the other hand this felt much more like a regular job and I couldn't make all the decisions I was normally able to make. So I still want to work for end clients, but I want to do it for a reasonable wage and with the feeling of having created a web site that will help the client increase his/her business. So how does one go about this while maintaining high standards?

Let's join our efforts, let's educate our clients

I think it's about time for us passionated developers (and designers) to unite. I think it's the time to create a foundation dedicated to educate clients and to tell them the truth about web development. Let us talk to them in a language that makes sense, one that isn't filled with technical overhead. Let us work with illustrations and let's maintain a list with the most common questions clients have about pricing, technology, what can be done and what can't. Let us create something we can referr to when clients demand WYSIWYG editors, or don't understand the difference between table based layouts and ones that rely on CSS. Let us put up example's and demos demonstrating the difference between good and pure quality sites. But most importantly, let us KISS ; ). As soon as articles gets longer then 1 page or get filled with technology abbreviations something goes wrong.

So let me ask, who would be interested in such a project? This isn't something I could do alone in my spare time. It would need a couple people who are willing to donate content, a skilled illustrator and somebody helping me to create a little CakePHP CMS for it. We would also need a name for the project, maybe something like "The truth about web development"? We could also need the support of some of the leading people in the field, and a good amount of blogsphere buzz. Let me know what you guys think and if you would be willing to help.

--Felix Geisendörfer the_undefined

PS: If you think I should stop ranting, write about PHP, and put my head in a cake, that's fine too. It will happen ;).

 

A lightweight approach to ACL - The 33 lines of Magic

Posted on 3/10/06 by Felix Geisendörfer

Ok, I just finished a terrible (extended) weekend that featured 12 hours of CSS coding. The only reason I didn't loose my sanity was that I finally decided to figure out what the heck is wrong with IE. Those of you who have to do get their hands dirty in the field of graphics, css, and other non-php work from time to time as well, make sure to check out Position is Everything at some point, it really helped me out quite a bit so far.

Anyway, that's not really what I want to talk about today. One of the topics I have been very silent about for months is ACL. At the end of May I was somewhat unhappy with some of the things regarding the CakePHP DB ACL implementation. And it wasn't until last week that I finally decided to implement some basic rights management in one of my applications again. So since I didn't want to bother to frustrate myself with Cake's ACL again, I started to roll my own solution. While I was happily hacking away at some code, I suddenly realized that there were a lot of familarities between the code I was writing and the way ACL was working. A couple minutes later and I was already convinced that I basically had created 33 lines of code giving me pretty much all the flexibility CakePHP's well over 500 lines would ever give me.

But let me go a step back and explain my initial idea. My basic plan was to have a User belongsTo Group relationship and that each group entry in my database would have a field listing the controller actions the members of this group would be allowed to access. When helping another company to get their CakePHP based CMS done before the deadline I saw them using a Controller:action style syntax to do this which I liked. I modified it a little bit and came up with a Syntax like this:

Posts:index,Posts:view,Posts:admin_edit,Articles:index,...

But since I felt it was too much work to type in all Controller actions for the admin account I decided to create some wildchar functionality:

Posts:*,Articles:*

or even shorter:

*:*

But since I wanted the visitors of the page to be able to use any Controller action besides the ones starting with 'admin_', I had to add negative statements as well:

*:*,!*:admin_*

That's when I realized, wait, that's essentially the same thing as ACL. You start with some basic statement like DENY or ALLOW ALL at the beginning and then go down the tree (or the string) for the specific rules. All rules farther to the right in the ACL string will overwrite the ones farther left. So if you start out by saying Posts:* but add a !Posts:secret somewhere down the road, it means the group can access all Posts actions besides 'secret'. Or a little more creative set of rules could look like this:

*:*,!*:admin_*,*:admin_index

But since I wanted even more control, I decided to add an ACL string to my user table as well so I could make exceptions on a per-user basis, even if all users belong to the same group. The basic logic I used for that was to first check the access the User group had to a certain action, and then use this value as the default value for the user-specific check. That means if the group says yes and the user has no rule matching the current Controller:action, he's allowed to request it. But if he has a matching rule, this rule is used to determine the outcome regardless of the group's permission.

Ok, at this point I've got to disappoint you guys a little bit. I'm not quite ready to release my SimpleAuth / SimpleAcl class I'm using right now quite yet. The reason for this is that there is a very cool Security class coming with Cake 1.2 and I really want to make use of it as well. If you want the code anyway, I'll put it in Cake bin - it's fully documented and should be ready to go, but I won't be able to give you much suppport on it. But what I'll give you, are the 33 lines of Magic code I was talking about, the ones taking apart a given set of $rules in order to determine if an $object is allowed to access a certain $property:

function requestAllowed($object, $property, $rules, $default = false)
{
    // The default value to return if no rule matching $object/$property can be found
    $allowed = $default;
   
    // This Regex converts a string of rules like "objectA:actionA,objectB:actionB,..." into the array $matches.
    preg_match_all('/([^:,]+):([^,:]+)/is', $rules, $matches, PREG_SET_ORDER);
    foreach ($matches as $match)
    {
        list($rawMatch, $allowedObject, $allowedProperty) = $match;
       
        $allowedObject = str_replace('*', '.*', $allowedObject);
        $allowedProperty = str_replace('*', '.*', $allowedProperty);
       
        if (substr($allowedObject, 0, 1)=='!')
        {
            $allowedObject = substr($allowedObject, 1);
            $negativeCondition = true;
        }
        else
            $negativeCondition = false;
       
        if (preg_match('/^'.$allowedObject.'$/i', $object) &&
            preg_match('/^'.$allowedProperty.'$/i', $property))
        {
            if ($negativeCondition)
                $allowed = false;
            else
                $allowed = true;
        }
    }        
    return $allowed;
}

As you see, this is not specific to Controller actions. This can be used to control access on any kind of objects like Models, or other things. If you are familiar with CakePHP's ACL, you'll know there is nothing this does that CakePHP couldn't do. But what really makes me happy about this solution is the simplicity behind it. You don't have to really study ACL to grasp how it works, neither to you have to get into Modified Preorder Tree Traversal nor do you have to plan complicated Model-Aro-Aco relationships. You simply add a field called 'rules' to the Model (table) you want to control access on, and use the function to perform your security checks.

Some of you might point out performance issues, or the fact that the rights field shouldn't really be mixed in with the other Model fields. Heck, even all the rules should be seperate entries if you want to go for really high database normalization. But that's not what this solution is about, this solution is about simplicity. It's about being able to grasp the entire security concept in less then 5 minutes, avoiding all the dangerous complexity people usally tend to bring into this field. If you want to optimize, normalize or add more complexity in general, feel free to do so and let me know about the outcome ; ). But I think this is going for what most of us Baker's need in our daily kitchen work.

So, write a comment if you like this approach or if you see some issue with it, so I can make a fix before releasing the Auth/Acl bundle of 2 drop-in components at some point soon.

--Felix Geisendörfer aka the_undefined

PS: I got my car back this weekend, so the long promised cake party should be happening this week for sure! Which reminds me, Thursday is my birthday, so maybe I don't even have to buy the cake myself ; ).

 

Learning from the CakePHP source code - Part II

Posted on 29/9/06 by Felix Geisendörfer

Sorry this post took me a while. The last couple days have been sort of busy and a lot of things on my lists didn't get done. Anyway, I'll continue right where I stopped, with the Dispatcher:

The heart of CakePHP - The Dispatcher

In the previous post I was showing how to use the Dispatcher::dispatch() function. Now what's more interesting, is what it actually does and in what order.

  1. Build the $params array. The first thing the Dispatcher::dispatch() function does is to perform an array_merge between $this->parseParams($url) and $additionalParams. Dispatcher::parseParams() is quite interesting as well. The first thing it does is to create a new Instance of the Router and call Router::parse($from_url). This returns an array with fields like 'controller', 'action', etc.. After that all variables from $_GET, $_POST and $_FILE are being merged into the $params array as well and finally returned to the Dispatcher function.
  2. Find the base url: The next thing Dispatcher::dispatch() does, is to find the base url, that all controller actions, routes, etc. happen within. This url is then assigned to $this->base. All the base url retrieving logic is implemented in Dispatcher::baseUrl().
  3. Load/Include the requested Controller: From now on I'll focus on the most important things going on because it would be insane to go into every little detail. The next block of code basically sees if $params['controller'] is emtpy, and if yes object::cakeError() is invoked to show the 'missingController' page all of us know. If the controller field is not empty, the first thing CakePHP tries to do is to load this controller (via loadController) from within /app/controllers. If that fails, CakePHP looks if there is a plugin with the name of $params['controller'] (via loadPluginController), and if not, missingController is invoked as well.
  4. Possible Plugin Logic: In case there was a plugin found, the next thing that happens is some plugin logic that does not get executed for normal Controller requests. It's basically some $params shifting magic (Dispatcher::_restructureParams()), and most notably the point where loadPluginModels() get's executed.
  5. Executing the Admin Route: If you have CAKE_ADMIN defined in your /app/config/core.php, then this is the point where the 'admin_' prefix (or whatever CAKE_ADMIN is set to) is added to the $params['action'] parameter. And here you also have a check that protects CAKE_ADMIN routes as private actions. This basically means if somebody would call /posts/admin_index instead of /admin/posts/index he wouldn't get very far.
  6. Render a possible missingController error: If at some point in the code above $missingController was set to true, this is the point where $this->cakeError('missingController', ...) is invoked. If not, $controller is being assigned a new instance of our $ctrlClass.
  7. Action exists? Action private?: The next couple of lines are dedicated to perform some checks on the $params['action'] parameter. The following things happen in the order I mention them: If $params['action'] is empty, it is set to 'index' instead. If the Controller has an action with the name $params['action'] but it starts with '_', $privateAction is set to true. If the Controller does not have an action $params['action'], or it happens to be named: 'beforeFilter', 'beforeRender' or 'afterFilter', $missingAction is set true.
  8. Set Controller variables: Now it's time for all of the Controller variables to be set with interesting information. This includes the 'autoRender', 'base', 'here', 'webroot', 'params', 'action', 'data', 'passedArgs', 'autoLayout', 'webservices' and 'plugin'.
  9. Load Components & Models: The Dispatcher now calls $controller->_initComponents() which attaches all components as references to the Controller object, but does not yet call their startup() function. After that, the same thing happens with Models when calling $controller->constructClasses().
  10. Render possible missingAction/privateActione errors: If there had been a missingAction or privateAction error before, this is the point where $this->cakeError() get's called to render those.
  11. Invoke the controller: The last call in Dispatcher::dispatch() goes to Dispatcher::_invoke(), but since it's still the same operation we are doing, I'll continue writing about things like before. The _invoke function directly executes Dispatcher::start(), which does the following things: If Controller::beforeFilter is a string/array, call the/all function(s) in this string/array (Warning: This is depracted!). Afterwards Controller::beforeFilter() get's executed without further checking. And the last thing happening in Dispatcher::start() is that all component's startup() functions get called. So back in Dispatcher::_invoke() there comes a check whether we are scaffolding or not, and if no, our Controller action get's called via call_user_func_array. Then, if $controller->autoRender is set to true, $controller->render() is called, and finally the afterFilter get's executed. Depending on whether there was some rendering going on or not either the raw output or the action's return value are getting returned to Dispatcher::dispatch() which directly returns this value back to the point where the initial call came from.

That's it, you've just witnessed the miracle of birth ... er dispatching. I've skipped some minor things here and there, but in general this together with Part I should be a good guide to the things happening before all your wonderful application code kicks in. Of course, there is a lot more like all the Model and View stuff. But talking about all of this would take for ever, bore you to death and would only be useful to very few of us. However, if you guys would be interested in it, I would post a Part III where I would just do some pointing at classes and code snippets a CakePHP user should have read to get a better understanding of the things going on in the background.

Oh, and today is probably also going to be the day I'll buy myself some yummy Cake ... : /. I'm not sure if I'll be able to take the picture and everything today, but I'll try my best. Hmm ... after that I should probably close this blog since nobody is going to hire me in the field of web development any more and I'll have to look for a new area to work in : /.

--Felix Geisendörfer aka the_undefined

 
51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59