David Golding



Using the File Functions in CakePHP

By David Golding

In CakePHP, a very useful set of functions is included but which get little documentation. I had combed the web looking for something useful about how to use the Cake File functions but got little concise or tutorial-based help. After working with the File functions for a little bit now, I figured an explanation might be useful.

Add.ctp View File

Let’s do a simple file upload. Let’s assume that I’m creating blog posts (sorry if using blogs as examples is overkill, but overall I think everyone gets it). For each blog post entry I’d like to upload an image file to match it. In the views/posts/add.ctp file, I have the following basic code:

<h1>Add Post</h1>
<?=$form->create('Post',array('type'=>'file'));?>
<?=$form->input('date',array('label'=>'Publication Date '));?>
<?=$form->input('headline');?>
<?=$form->input('content');?>
<?=$form->input('image',array('type'=>'file'));?>
<?=$form->end('Submit');?>

In the Controller

Now, the most basic upload using the File functions is going to occur in the controllers/posts_controller.php file.

Everything should run like a normal Add function, except I need to actually do the upload of the file.

if ($this->data['Post']['image']) {

$file = new File($this->data['Post']['image']);
$ext = $file->ext();

if ($ext != 'jpg' && $ext != 'jpeg' && $ext != 'gif' && $ext != 'png') {
$this->Session->setFlash('You may only upload image files.');
$this->render();
} else {
$date = $this->data['Post']['date'];
$filename = $date['year'].'-'.$date['month'].'-'.$date['day'].'-post-image.'.$ext;

$data = $file->read();
$file->close();

$file = new File(WWW_ROOT.'/img/'.$filename,true);
$file->write($data);
$file->close();
}
}

Let me explain what is happening here. When the user chooses a file to be uploaded in the HTML form, the link to that file is set through the $_POST variables, and in Cake, as will all form posts, these get parsed into the $this->data array.

The first step, then, is to create a new instance of the File class so we can use those functions.

$file = new File($this->data['Post']['image']);

From here on out, I can pull all sorts of information about this file by using the available Cake functions. You’ll notice that I pulled out the file extension quite easily by using the File->ext() function:

$ext = $file->ext();

If I were to use an echo command in PHP, I would get

jpg

for a JPG file upload.

To write the contents of this file to the server, I need to first fetch all the data in the file.

$data = $file->read();

Now all the contents of the file are held in the $data variable. To write this to the server, I’ll need to create a new file instance, create the file in the appropriate directory (in this case, the webroot/img folder), and dump the contents of $data into that file.

$file->close(); //close out of the user's file

$file = new File(WWW_ROOT.'/img'.$filename,true);

Notice that in creating the new instance for the File class, I’ve used the Cake global variable, WWW_ROOT and I’ve appended the /img directory plus the desired filename. Also, I’ve set create new file to true.

Now the $file variable is set to the new object, which in this case is the empty file on my server stored in the webroot/img directory named whatever is in $filename. By using the write() function, I can plop $data into that file:

$file->write($data);

The rest of the script is just closing out everything.

Other Functions

So how about it? Much, much easier than doing all the classic fopen and fread, fputs functions in PHP. Of course you can branch out and do more as well as error checking using other parameters in the functions themselves.

The CakePHP API contains a list of the available File and Folder functions. By using them like you’ve seen me do above, you can spare yourself superfluous coding.

For example, if I wanted to test if the file by that filename already exists on the server, I can enter:

$id = intval(rand());
$filename = $id.'-post.'.$file->ext();
$folder = new Folder(WWW_ROOT.'/img');
while ($folder->find($filename) == $filename) {
$id++;
$filename = $id.'-post.'.$file->ext();
}

Or if I wanted to delete a file on the server where $file has been set as the class object of the file to be deleted:

$file->delete();

So explore the other File functions and enjoy!


Lessons Learned from Anno Domini 2.0

By David Golding

With the latest release of Anno Domini to version 2.0 I’ve had a lot to be excited about. First, in the couple months that Anno Domini 1.0 has been on CakeForge it has consistently ranked as one of the top projects. Somewhere around 3,000 downloads have come from CakeForge and another few thousand elsewhere. But I must confess that 1.0 had a lot of hacks in it. At the time I was fairly familiar with CakePHP but still pulled a lot of PHP hacks to get the dang thing to work. Since then both myself and CakePHP have improved and it became increasingly clear that some important changes would have to happen to Anno Domini before it would tank and deprecate itself against newer versions of both PHP and CakePHP.

So I started from the ground up and rebuilt Anno Domini on Cake 1.2 beta. Here are some important lessons I learned while doing this project.

Working in CakePHP 1.2 is faster

Getting started in CakePHP 1.2 early on had me worried about all the new functions or installation procedures, etc. And, naturally, bringing over projects built in 1.1 would have deprecated functions or helpers which I was not at all excited about hunting down and fixing.

However, I’ve been working all my new projects in 1.2 for months now and the benefits for biting the bullet and moving over certainly outweigh the costs. The overall experience, the gestalt if you will, of working in 1.2 is so superior to working in PHP by hand or from earlier versions of Cake. I found myself assuming that there existed a helper or a component which could cut down on the amount of work I’d have to put into almost every aspect of the application because most of the time I’d be right. If you haven’t begun working in 1.2, and even more importantly, if you’re still just doing things in PHP alone, now is the time to begin working in Cake 1.2 beta.

Fat Models do more than trim the controller

I took Daniel Hofstetter’s advice to look for opportunities to put functions in the model rather than in the controller. Not only did this trim down my controllers by reducing redundancy in the app but the overall structure of the organization of the app was improved. I think when we put everything in the controller, the organization becomes much more one-dimensional. But by beefing up the models my controller was trim and it allowed me to break out into more areas to build chunks of the app. This one intentional realignment of site organization, I think, can lead to a more improved layout to app resources.

Doing things right can dramatically reduce coding time

Following the conventions in Cake really does speed up programming. In the end, Anno Domini 1.0 took about three to four weeks to complete, somewhere around 40 to 60 hours to finish. True, learning from past mistakes cuts down on redesigning the application, but I’m confident the disparity between production time on Anno Domini 1.0 and 2.0 owes more to Cake 1.2 and following Cake conventions than my improved awareness of what worked and what didn’t in 1.0. Version 2.0 took me around 18 hours to complete, about two full work days. That’s around a third to half the time as the first version. By the end of the project, I really had begun to see the benefits of strictly adhering to Cake conventions and not to hacks.

One example was in the component class. I had wanted to access the model directly from the component but had read in many places that such was not the design of the Cake structure. I had to settle for using the $this->requestAction() in the controller to return some basic model finds but later on noticed that those controller functions were useful elsewhere. By sticking to the Cake convention, I was spared some redundancy.

So, there are certainly some more nitty-gritty details I got out of this project, but I hope these facts illustrate some benefits for the rest of you. Adhere to convention, new releases of Cake, and make your models nice and fat and you will be rewarded :)


News on the Book

By David Golding

Well, I’m sorry about the delay on this, but I’m really excited about the outcome of the book. Apress has picked up the book and will be publishing it in July. I had hoped to finish it before then and get it into your hands sooner, since I am well aware of the need for new formats for Cake documentation, and I know that many of you like the possibilities of Cake but want a quicker introduction to it. But to release the book with a credible publisher right off the bat is more important for the community and it provides me the time and means to finish the book. I’ll post more details as the book progresses, but rest assured, I’ll have it out there as soon as possible. You can check out its listing on Amazon.com.


Beginning CakePHP: From Novice to Professional by David Golding

David Golding

A blog about CakePHP, web design, and grad studies in religion. © 2008, D. Golding