Full CakePHP 1.2 App Part 4
As the title aptly suggests this is article 4 in my series of creating a full online DVD Catalog Application using CakePHP 1.2. Last time I quickly setup the Types and Locations Controller and Views so that I could start to add more data to the database.
In this article I'm going to create the DVDs controller and View files so that I can start to add / edit / delete DVDs from the database (I've gone for a Tarantino theme to begin with). This is going to be a bit more advanced because I am going to be uploading images to the server and I'm also going to use the jQuery Javascript library to progressively enhance a few forms and to stripe all the tables on the fly.
DVDs Controller and Views
First thing to do as usual is to create the Controller (dvds_controller.php) for the DVD Model along with the related Views for the actions.
<?php
/**
* Dvds Controller
*
* file: /app/controllers/dvds_controller.php
*/
class DvdsController extends AppController {
// good practice to include the name variable
var $name = 'Dvds';
// load any helpers used in the views
var $helpers = array('Html', 'Form', 'Javascript', 'Misc');
// global ratings variable
var $ratings = array('0'=>'0', '1'=>'1', '2'=>'2', '3'=>'3', '4'=>'4', '5'=>'5', '6'=>'6', '7'=>'7', '8'=>'8', '9'=>'9', '10'=>'10');
/**
* index()
* main index page for dvds
* url: /dvds/index
*/
function index() {
}
/**
* view()
* displays a single dvd and all related info
* url: /dvds/view/dvd_slug
*/
function view($slug) {
}
/**
* admin_index()
* main index page for admin users
* url: /admin/dvds/index
*/
function admin_index() {
}
/**
* admin_add()
* allows an admin to add a dvd
* url: /admin/dvds/add
*/
function admin_add() {
}
/**
* admin_edit()
* allows an admin to edit a dvd
* url: /admin/dvds/edit/id
*/
function admin_edit($id = null) {
}
/**
* admin_delete()
* allows an admin to delete a dvd
* url: /admin/dvds/delete/1
*/
function admin_delete($id = null) {
}
}
?>
Here are the View files that I need to create:
- /app/views/dvds/index.ctp
- /app/views/dvds/view.ctp
- /app/views/dvds/admin_index.ctp
- /app/views/dvds/admin_add.ctp
- /app/views/dvds/admin_edit.ctp
DVD Index
The index() function will be similar to the past ones I've created, retrieving the DVDs from the database that are currently active and also order the DVDs in alphabetical order. CakePHP's find methods are quite advanced and allow you to pass a number of arguments that will filter and sort the data from the database. For a full rundown of the parameters you can pass check out the Models Chapter in the cookbook.
// file: /app/controllers/dvds_controller.php
function index() {
// get all dvds from database where status = 1
$dvds = $this->Dvd->findAll("Dvd.status=1", null, "Dvd.name");
// save the dvds in a variable for the view
$this->set('dvds', $dvds);
}
The View is pretty similar to the previous index views with a few additions. I'm going to link to the edit page of the Format, Type and Location and I'm also going to leave out the coding for the striped table this will be accomplished with jQuery which I'll go through later in the article.
// file: /app/views/dvds/admin_index.ctp
<div class="dvds index">
<h2>Dvds Admin Index</h2>
<p>Currently displaying all DVDs in the application</p>
<?php
// check $dvds variable exists and is not empty
if(isset($dvds) && !empty($dvds)) :
?>
<table>
<thead>
<tr>
<th>Name</th>
<th>Format</th>
<th>Type</th>
<th>Location</th>
<th>Rating</th>
<th>Created</th>
<th>Modified</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php foreach($dvds as $dvd): ?>
<tr>
<td><?php echo $dvd['Dvd']['name']; ?></td>
<td><?php echo $html->link($dvd['Format']['name'], array('controller'=> 'formats', 'action'=>'edit', $dvd['Format']['id'])); ?></td>
<td><?php echo $html->link($dvd['Type']['name'], array('controller'=> 'types', 'action'=>'edit', $dvd['Type']['id'])); ?></td>
<td><?php echo $html->link($dvd['Location']['name'], array('controller'=> 'locations', 'action'=>'edit', $dvd['Location']['id'])); ?></td>
<td><?php echo $dvd['Dvd']['rating']; ?></td>
<td><?php echo $dvd['Dvd']['created']; ?></td>
<td><?php echo $dvd['Dvd']['modified']; ?></td>
<td>
<?php echo $html->link('Edit', array('action'=>'admin_edit', $dvd['Dvd']['id']) );?>
<?php echo $html->link('Delete', array('action'=>'admin_delete', $dvd['Dvd']['id']), null, sprintf('Are you sure you want to delete Dvd: %s?', $dvd['Dvd']['name']));?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php
else:
echo 'There are currently no DVDs in the database.';
endif;
?>
<ul class="actions">
<li><?php echo $html->link('Add a DVD', array('action'=>'add')); ?></li>
</ul>
</div>
DVD Admin Add
The admin_add() function will be quite complex so I'll break it down a little and first get the functionality working without a file upload. The main logic will be the same as any add function, I'll create a slug from the name, attempt to save the data and finally redirect the admin with an error message.
// file: /app/controllers/dvds_controller.php
function admin_add() {
// if the form data is not empty
if (!empty($this->data)) {
// initialise the Dvd model
$this->Dvd->create();
// create the slug
$this->data['Dvd']['slug'] = $this->slug($this->data['Dvd']['name']);
// check for a dvd with the same slug
$dvd = $this->Dvd->find('first', array(
'conditions' => array(
'Dvd.slug'=>$this->data['Dvd']['slug'],
'Dvd.status' => '1'
)
));
// if slug is not taken
if(empty($dvd)) {
// try saving the format
if ($this->Dvd->save($this->data)) {
// set a flash message
$this->Session->setFlash('The DVD has been saved', 'flash_good');
// redirect
$this->redirect(array('action'=>'index'));
} else {
// set a flash message
$this->Session->setFlash('The DVD could not be saved. Please, try again.', 'flash_bad');
}
} else {
// set a flash message
$this->Session->setFlash('The DVD could not be saved. The Name has already been taken.', 'flash_bad');
}
}
// find dvd options in a list format
// new 1.2 feature, can also have 'count' and 'first'
$formats = $this->Dvd->Format->find('list');
$types = $this->Dvd->Type->find('list');
$locations = $this->Dvd->Location->find('list');
$ratings = $this->ratings;
// set the variables so they can be accessed from the view
$this->set(compact('formats', 'types', 'locations', 'ratings'));
}
In order to select the Format, Type and Location I will need a drop down select box, CakePHP has a few very nice features that will help with creating them. First the find('list') method will automatically create an array that will be used in the form. It seems that the new 1.2 version automatically detects the type of form inputs but just to show how to create one the manual way I've included the code below. The View file will be standard and will use the Form Helper to quickly create the form inputs, CakePHP will also automatically re-fill the form if any errors occur.
// file: /app/views/dvds/admin_add.ctp
<div class="dvds form">
<?php echo $form->create('Dvd');?>
<fieldset>
<legend>Add a Dvd</legend>
<?php
// create the form inputs
echo $form->input('name', array('label'=>'Name: *'));
echo $form->input('format_id', array('label'=>'Format: *', 'type'=>'select', 'options'=>$formats));
echo $form->input('type_id', array('label'=>'Type: *', 'class'=>'type_select'));
echo $form->input('location_id', array('label'=>'Location: *'));
echo $form->input('rating', array('label'=>'Rating:'));
echo $form->input('website', array('label'=>'Website URL:'));
echo $form->input('imdb', array('label'=>'Imdb URL:'));
echo $form->input('discs', array('label'=>'Number of Discs:', 'class'=>'tv_hide'));
echo $form->input('episodes', array('label'=>'Number of Episodes:', 'class'=>'tv_hide'));
?>
</fieldset>
<?php echo $form->end('Add');?>
</div>
<ul class="actions">
<li><?php echo $html->link('List DVDs', array('action'=>'index'));?></li>
</ul>
Like before I'm also going to setup a few validation rules so that I cannot add empty data to the database.
// file: /app/models/dvd.php // setup form validation for dvd var $validate = array( 'name' => array( 'rule' => VALID_NOT_EMPTY, 'message' => 'Please enter a Dvd Name' ), 'format_id' => array( 'rule' => 'numeric' ), 'type_id' => array( 'rule' => 'numeric' ), 'location_id' => array( 'rule' => 'numeric' ) );
Uploading Files
Once the add form is up and running I can now add the file functionality, there is just a few changes I need to make in the View and a bit more logic in the controller.
The first thing I need to do is make sure the form include the enctype attribute so that the server will save the file data, this is easily done by passing a variable in the form create() helper method:
// file: /app/views/dvds/admin_add.ctp
// this
<?php echo $form->create('Dvd', array('type'=>'file'));?>
// instead of
<?php echo $form->create('Dvd');?>
I also need to include the actual file input and this is done again by using the form helper:
// file: /app/views/dvds/admin_add.ctp
echo $form->input('File.image', array('label'=>'Image:', 'type'=>'file'));
I've used the input name File.image so that when the form is submitted the file information is stored in a seperate array to the actual DVD data, this makes it easier to upload the file and also makes sure that the validation is run on the DVD information. The form data looks like this:
Array
(
[Dvd] => Array
(
[name] => testing
[format_id] => 1
[...] => ...
)
[File] => Array
(
[image] => Array
(
[name] => desperado.jpg
[type] => image/jpeg
[tmp_name] => C:\server\tmp\phpCE.tmp
[error] => 0
[size] => 44218
)
)
)
Now I need to deal with the file upload in the Controller action. I've dealt with file uploads in a previous article of mine so I've copied the upload_files() function to my app_controller.php file.
I've created a private function called _upload_image() in my dvds_controller.php to process and upload the file from the form. First I check that a file has been selected for upload by checking the error variable in the file array, then I use my upload_files() function to try and upload the file to the server. If no errors exist then I can save the url if there are errors then they are saved to be displayed in the view.
// file: /app/controllers/dvds_controller.php
/**
* upload_image()
* private function to upload a file if it exists in the form
*/
function _upload_image() {
// init
$image_ok = TRUE;
// if a file has been added
if($this->data['File']['image']['error'] != 4) {
// try to upload the file
$result = $this->upload_files('img/dvds', $this->data['File']);
// if there are errors
if(array_key_exists('errors', $result)) {
// set image ok to false
$image_ok = FALSE;
// set the error for the view
$this->set('errors', $result['errors']);
} else {
// save the url
$this->data['Dvd']['image'] = $result['urls'][0];
}
}
return $image_ok;
}
I can then call this method from my admin_add() action to upload the file if it exists. The action now looks like this:
// file: /app/controllers/dvds_controller.php
function admin_add() {
// if the form data is not empty
if (!empty($this->data)) {
// check for image
$image_ok = $this->_upload_image();
// if the image was uploaded successfully
if($image_ok) {
// do same logic as before
}
}
// do same logic as before
}
To display any file upload error messages I've created a helper that will contain my custom functions that I may need in a View. One such function displays error messages if its passed either a string or an array. Create a new file called misc.php in the /app/views/helpers folder and use this code to create a new function.
<?php
/**
* MiscHelper Class
* has a few custom functions that are useful in a view
*
* file: /app/views/helpers/misc.php
*/
class MiscHelper extends AppHelper {
/**
* display_errors()
* displays a list of errors given an array or just a string
*/
function display_errors($errors) {
//init
$output = '';
$temp = '';
// if an array
if(is_array($errors)) {
// loop through errors
foreach($errors as $error) {
$temp .= "<li>{$error}</li>";
}
} else {
// save error
$temp .= "<li>{$errors}</li>";
}
// build up div
$output = "<ul class='flash_bad'>{$temp}</ul>";
return $output;
}
}
?>
This is just a simple function that will print out any error messages in an unordered list. It can be used by calling the function in a View like this:
// file: /app/views/dvds/admin_add.ctp
// if there was an error uploading the file then display errors here
if(isset($errors)) {
echo $misc->display_errors($errors);
}
Here's a screenshot of the final add form:
DVD Admin Edit
The admin_edit() function will be very similar to the add function with a few slight differences, first I must check that the id being passed is valid and if not I'll redirect with a flash error message.
// file: /app/controllers/dvds_controller.php
// if the id is null and the form data empty
if (!$id && empty($this->data)) {
// set a flash message
$this->Session->setFlash('Invalid Dvd', 'flash_bad');
// redirect the admin
$this->redirect(array('action'=>'index'));
}
This code is standard for an edit function and if you've ever used the bake script to generate your Controller code you will have something similar. Next I'm going to check if the form has been submitted, if it has then I'm going to process the form in the exact same way as the admin_add() function so I can just copy and paste that code here. If the form has not been submitted I can retreive the DVD from the database and save it to the data array.
// file: /app/controllers/dvds_controller.php
function admin_edit($id = null) {
// if the id is null and the form data empty
if (!$id && empty($this->data)) {
// set a flash message
$this->Session->setFlash('Invalid Dvd', 'flash_bad');
// redirect the admin
$this->redirect(array('action'=>'index'));
}
// if the form was submitted
if (!empty($this->data)) {
// code from admin_add() goes here
} else {
// find the DVD from the database and save it in the data array
$this->data = $this->Dvd->read(null, $id);
}
// find dvd options from database in a list
$formats = $this->Dvd->Format->find('list');
$types = $this->Dvd->Type->find('list');
$locations = $this->Dvd->Location->find('list');
$ratings = $this->ratings;
$this->set(compact('formats','types','locations', 'ratings'));
}
In the View file for the admin_edit() function I'm going to display the current uploaded image (if one exists) and I also need to include the id of the DVD in the form. This ensures that CakePHP knows that this is an edit form and will change the correct DVD in the database. Here's a snippet of code from the Admin Edit View:
// file: /app/views/dvds/admin_edit.ctp
// include the id of the DVD as a form input
// CakePHP will automatically create this as a hidden element
echo $form->input('id');
// display image if it exists
if(!empty($this->data['Dvd']['image'])): ?>
<div class="input">
<label>Current Image:</label>
<img src="/<?php echo $this->data['Dvd']['image']; ?>" alt="Dvd Image" width="100" />
</div>
<?php endif;
Here's a screenshot of the Admin Edit View:
DVD Admin Delete
For the delete action I'm going to change the status of the DVD from '1' to '0' in the database, this way I dont actually delete anything and if something goes wrong I can get my data back from the database. This is know as a 'soft delete' and I'm going to use this method throughout the application.
// file: /app/controllers/dvds_controller.php
// set the id of the dvd
$this->Dvd->id = $id;
// try to change status from 1 to 0
if ($this->Dvd->saveField('status', 0)) {
}
Using jQuery in CakePHP 1.2
I've been using jQuery for some time now and its a great library to use in everyday web development. The first thing to do is download it from the jQuery site. I've downloaded the packed version, which is a smaller compression version which is great for keeping downloads to a minimum.
Place the library in /app/webroot/js and open up the default layout file located at /app/views/layouts/default.ctp. If this file does not exist then you will need to copy the one located at /cake/libs/view/layouts/default.ctp into your layouts folder in your app directory. This just makes sure that your default.ctp is the one that Cake will use.
Next I need to link to the jQuery library if the Javascript Helper is active in a Controller. To create a link I can use the link() function to make the library active in the View. As well as the jquery-1.2.3.pack.js I've created a new blank file called common.js, this will contain my javascript code to run on the page.
// file: /app/views/layouts/default.ctp
// if the javscript helper is set include the javascript library
if(isset($javascript)) {
echo $javascript->link(array('jquery-1.2.3.pack', 'common'), true);
}
When I want to use Javascript I must now load the Helper in the Controller. I've done this before with the HTML and Form Helpers I just need to add Javascript to the array like this:
// file: /app/controllers/dvds_controller.php
// load any helpers used in the views
var $helpers = array('Html', 'Form', 'Javascript', 'Misc');
Here I've checked that the Javascript files are being loaded correctly using the Firebug Firefox Extension:
Striping Tables with Javascript
Now that I have jQuery at my disposal I'm first going to use it to stripe the even rows in any table that I assign a class='stripe'. This is very handy because I dont need to hard code this functionality with PHP which makes my code a little cleaner.
Open up the common.js file and enter the code below. The first ready() function is the base of all jQuery code and simply executes the code when the page has fully loaded. To read more about the basics of jQuery check out the documentation on the website. I'm going to target any Table that has a 'stripe' class and select all the even rows. Once they have all been selected I'm going to add a 'altrow' class to the row, once this is done I can then use CSS to style the class with a different colour.
// file: /app/webroot/js/common.js
// when the document is ready
$(document).ready(function(){
// stripe all the tables in the application
$('table.stripe tr:even').addClass('altrow');
});
jQuery has extremely powerful 'selector' methods and also uses 'method chaining' to make things easy to select and manipulate. With just one line of code I can stripe any table throughout the application with a single class.
Here is the CSS I've used and below is a screenshot of the striping in action:
// file /app/webroot/css/cake.generic.css
table tr.altrow td {
background: #ebebeb;
}
Enhancing Forms
Currently the Admin Add Form displays two inputs (Number of Disks and Number of Episodes) that will only be used if the DVD is a 'TV Show'. So I'm going to use Javascript to hide the inputs if the DVD is a 'Film' and show the inputs if 'TV Show' is selected in the drop down menu.
To target these two form inputs I've added a 'tv_hide' class when creating the form like so:
// file: /app/views/dvds/admin_add.ctp
// add a class to the form input
echo $form->input('discs', array('label'=>'Number of Discs:', 'class'=>'tv_hide'));
echo $form->input('episodes', array('label'=>'Number of Episodes:', 'class'=>'tv_hide'));
This will produce the following HTML code:
<div class="input" style="display: block;"> <label for="DvdDiscs">Number of Discs:</label> <input type="text" id="DvdDiscs" value="" maxlength="4" class="tv_hide" name="data[Dvd][discs]"/> </div>
Now I need to attach a Javascript event handler to the 'Type' drop down input that will find out what type has been selected and will show or hide the form inputs accordingly.
// file: /app/webroot/js/common.js
// add event handler to type select form input
$('.type_select').change(function(){
// get the value of the selected option
var type = $(this).find('option:selected').text();
// log the type for testing purposes
console.log( type );
// if the type is a tv show
if(type == "TV Show") {
// fadein the form inputs
$('.tv_hide').parent().fadeIn();
} else {
// fade out and hide the form inputs
$('.tv_hide').parent().fadeOut();
}
});
Because I've added a class to the form input and not the surrounding div I make use of the .parent() method to select the correct div. This example also shows method chaining in action.
Its not quite finished yet because I need to accomodate for the Admin Edit Form and make sure that the form inputs are already displayed or hidden depending on the 'Type' the edited DVD has. To do this I use the same techniques as before and get the selected DVD type and if it doesn't equal 'TV Show' then I hide the form inputs with a 'tv_hide' class.
// file: /app/webroot/js/common.js
// get value of selected type
var current_type = $('.type_select option:selected').text();
// if the selected option is not 'TV Show' then hide the tv options
if(current_type != "TV Show") {
// hide the tv elements from form
$('.tv_hide').parent().hide();
}
Wrapping Up
I've managed to cover quite a lot of ground in this article, I've setup yet another Controller and related View files (a familiar process by now I hope) and included the ability to upload files to the server and save the URL to the database.
I've also covered including jQuery in the application and using some Javascript voodoo to stripe tables and enhance forms by showing and hiding inputs depending on a selected DVD Type. If you do have any problems with anything I've covered let me know and I'll try to sort you out.
Source Code
The source code for this article can be downloaded using this link. If these articles are helping you out why not consider donating I can always use a beer! :)
Next Article
In the next article I'll be dealing with Genres and this will include setting up the Controller and View files and delving into how CakePHP deals with the hasAndBelongsToMany relationship between DVDs and Genres. At the end of the article you will be able to add / edit / delete Genres and also assign DVDs to multiple genres so that when a Genre is viewed all the DVDs will be retrieved and displayed.
Comments
yodiaditya (18/05/2008 - 12:00)
Need one week and finally i find this amazing article!
good job dude....
May i request about theming in cakePHP 1.2? that's will help me so much!
keep creating best tutorial about cakePHP
yodiaditya (18/05/2008 - 21:27)
When trying add dvd, I have some errors with ratings like this :
"Undefined property: DvdsController::$ratings"
Looks like the problem in admin_add(),
"
// find dvd options in a list format
// new 1.2 feature, can also have 'count' and 'first'
$formats = $this->Dvd->Format->find('list');
$types = $this->Dvd->Type->find('list');
$locations = $this->Dvd->Location->find('list');
$ratings = $this->ratings; <--- this problem
"
Have solution? Many thanks
yodiaditya (18/05/2008 - 21:37)
but, after i change :
$ratings = $this->Dvd->find('list');
There no error and I'm still doubt it.
James (19/05/2008 - 06:01)
@yodiaditya: whoops must of missed a global variable at the top of the page, will correct this later today. But quickly $this->ratings is an array e.g.
var $ratings = array('0','1','2','3','4','5');
Thanks for the comments, I think templating is gonna be an article near the end of the series so stay tuned.
yodiaditya (22/05/2008 - 23:40)
Adding that var $rating then i get correct now. Thanks for solution. I'm still waiting your next article
aluMi (15/08/2008 - 01:45)
I got Undefined property: Dvd::$Format.
I've checked Dvd model, Format model. All correct.
Any solution? Where should I recheck now?
James (19/08/2008 - 05:47)
@aluMi: just got your comment, not too sure whats going wrong. First print out the actual data array to the screen and make sure that the DVD is getting the related Format data, if not double check the recursive variable to ensure that your find() calls are grabbing related info as well. Let me know how it goes.
yaqoob (20/08/2008 - 16:15)
a small tip to adjust your tutorial - the function for uploading files in your "Uploading Files and Images with CakePHP" tutorial is named 'uploadFiles' and in this one is called 'upload_files', this causes some errors when you just copy&paste the functions
once more, thanks for awsome tutorials :) I will be most probably having some questions soon, then I'm gonna write you an email
Andy (07/09/2008 - 12:18)
I put file management in the model class. IMHO it's part of the model. beforeValidate (for errorchecking, such as mimetype, filesize, upload errors...) and beforeSave (for storing the file at the right place) are perfectly suited for file uploading.
James (08/09/2008 - 03:08)
@Andy: yeah file management would probably go in the Model but I wanted to build something I could port to other projects and would be best as a stand alone Component. Although the before Filters are a great idea for file validation.
aluMi (08/09/2008 - 21:10)
I made it. It was my typo in the Model file. Thank you for your responding.
BTW, great works you've done here.
James (09/09/2008 - 04:37)
@aluMi: Hey glad you got it sorted!
Stefan (24/09/2008 - 04:50)
Hi James, great Series of Tutorials you are publishing here. Learned a lot! One thing I probably missed is the handling of the "slug" in the admin_edit function. I run into trouble when editing a DVD-Record because oviously the slug is already in the Database if you do not change the title. I think this can be fixed by replacing
if(empty($dvd)) {
with
if(empty($dvd) or $dvd['Dvd']['id'] == $this->data['Dvd']['id']) {
in the dvds_controller.php
James (24/09/2008 - 05:01)
@Stefan: Thanks for getting in touch, I'm sure I coded that possibility but I may have missed it but your solution is the best way to go.
onokung (14/10/2008 - 10:47)
Hi james, I did follow your code on this article when I add new record dvd i found waring is "Warning (2): Cannot modify header information - headers already sent by (output started at D:\AppServ\www\dvdcatalog\app\controllers\dvds_controller.php:118) [CORE\cake\libs\controller\controller.php, line 587]
"
do you know what is wrong and how to fix it.
Thank you.
James (14/10/2008 - 10:56)
@onokung: Hey, thanks for commenting. The "Cannot modify header information" error usually occurs when you have outputting something in your controller before the view is displayed. Check your controller for any output, usually pr() statements and see if that fixes things.
onokung (15/10/2008 - 11:00)
Hi james, thanks for your help but I found my wrong in my code that in my dvds_controller.php it had a white space after php tag ex. <?php ?>....white space here... :o)
Kener (04/11/2008 - 09:11)
Great tutorial, thanks a lot James!!
Peter Ojiambo (08/11/2008 - 17:27)
Hi, just started learning to use cakephp and stumbled on your tutorial that looks great and will begin learning from it asap. I get this error when trying to use a form helper instead of normal html and i was wondering if you could help me work out the bug in my code and come to a solution?
Call to undefined method FormHelper::create()
the error seems to originate from one of my views, but i see that in your tutorial you have used the helper in the same way i intend. Thanks for all the good work on the tute and thanks in advance!
Peter
James (10/11/2008 - 04:53)
@Peter: Hey, thanks for commenting. Double check you've included the helper in your Controller file, at the top you should have a variable like this with an array of all the helpers your going to use:
var $helpers = array('Html','Form');
Hope that helps.
Pfadi Frauenfeld (12/11/2008 - 15:15)
I just love CakePHP as it speeds up development and facilitates maintainance through its MVC-Pattern. One thing that's missing and not quite that easy is record-based ACL and a good administration tool/template for user and permission management.
Barbara (15/11/2008 - 09:37)
Great tutorial James!
Just a curiosity. Is there a particular reason to check for a dvd with the same slug in order to avoid duplicates? Is it also possible to check for a dvd with the same name?
'Dvd.name'=>$this->data['Dvd']['name']
Thanks
James (17/11/2008 - 04:46)
@Barbara: Hey, I've used the slug to check for duplicates because that's used in the view action to grab the dvd from the db, you could use the name of the DVD but you may run the risk of collisions if someone mistypes a DVD name by inserting two spaces or some other character that gets removed when a slug is created.
wawan (07/12/2008 - 13:24)
Hi James, I can't see image when running edit DVDs in admin_edit view. The code is :
<img src="/<?php echo $this->data['Dvd']['image']; ?> alt="Dvd Image" width="100" />
I have tried to change with src=".../.../myimage.jpg" but still not display. Then I change with <?php echo $html->image($this->data['Dvd']['image'],array('width' => 100)); ?>, the result is same. But when I change with <?php echo $html->image("myimage.jog",array('width' => 100)); ?> the image display correctly. Please tell me what why? thank
James (08/12/2008 - 04:56)
@wawan: hmm not too sure why its not working for you, double check your system for files and make sure that its uploading them into the correct directories. I generally try and reference all images from the root of the server so try and use absolute paths instead of relative ones.
Tomy (05/01/2009 - 09:55)
I have same problem with wawan..
I already double checked too..
James (06/01/2009 - 10:47)
@Tomy: hmmm I'll have a look and see if I can figure out whats going on, in the mean while download the next article's source code and see if that fixes the issue.
Mark (23/01/2009 - 11:01)
I'm getting a notice that of 'Undefined Variable: formats' in my admin_add.ctp. It doesn't seem to like receiving $formats for the options setting in the input in the add form. I've double checked the syntax in the compact function in the controller and both models.
James (26/01/2009 - 10:15)
@Mark: Hmm not too sure why your getting an "undefined index" error, maybe try and download the source code from the end or even try the next article's code. If your still having trouble email them over and I'll take a look.
Paulo Marcelo (17/03/2009 - 08:54)
It would be better
replace this:
$formats = $this->Dvd->Format->find('list');
$types = $this->Dvd->Type->find('list');
$locations = $this->Dvd->Location->find('list');
to this:
$formats=$this->Dvd->Format->find('list',
array('conditions' => array('Format.status' => '1')));
$types= $this->Dvd->Type->find('list',
array('conditions' => array('Type.status' => '1')));
$locations= $this->Dvd->Location->find('list',
array('conditions' => array('Location.status' => '1')));
Because the user will not see the types, formats and locations that the admin deleted(status=0).
James (21/03/2009 - 06:11)
@Paulo: Hey thanks for commenting, yeah adding in the conditional option to grab only the active lists makes perfect sense and I must of overlooked it. Good spot.
TanukiBZH (24/06/2009 - 18:00)
SQL Error: 1048: Column 'discs' cannot be null [CORE/cake/libs/model/datasources/dbo_source.php, line 525]
Error when you choose "Films" for Type because : "Episodes" and "Discs" are not null in the table DVDs.
This case is not treated in your code after adding Javascript on Type.
Tanukibzh (24/06/2009 - 23:40)
if you are not a virtual host then your url is http://localhost/dvdcatalog/ => you don't see picture.
else if you are a virtualhost with the url "http://dvdcatalog" there aren't problem of picture. you see images
James (25/06/2009 - 12:00)
@Tanukibzh: Thanks for commenting, I always use virtualhosts for my applications and therefore never tested it without using one. Thanks for bringing the error to my attention as well, will be useful for other people who encounter similar problems.
daniel (25/01/2010 - 01:22)
hi James, I have a problem when adding a new dvd. The image url was not inserted into the table. But the file was uploaded as i check for it. Wonder what has gone wrong?
James (26/01/2010 - 04:53)
@daniel: hmm not certain but try checking the "_upload_image()" method and check the line:
// save the url
$this->data['Dvd']['image'] = $result['urls'][0];
to ensure that the url is being saved properly.
daniel (27/01/2010 - 18:39)
hi James, thanks. Just got it to work. I messed up when setting up the $result['urls']
Munish Sharma (16/02/2010 - 02:09)
Awesome work with such description. I am learning cake these days and really you are so far the biggest resource for help. Want to ask one thing though, can you write a tutorial for a good signup page with css as finding problem in placing css in cake.
Fairlight (16/03/2010 - 03:34)
I was successfully able to implement all the above things you said as in the dvd controller except uploading the image.
Is it possible for u to create a tuts using component to upload image in the same project coz i am having a tough time doing it and it would be of great help if u are able to do it and guide me through it
James (17/03/2010 - 23:55)
@Fairlight: I've done a previous tut on uploading files and images so have a look for that one.
wonz (23/03/2010 - 06:19)
Hi i get a
SQL Error: 1048: Column 'discs' cannot be null [CORE/libs/model/datasources/dbo_source.php, line 525]
Query: INSERT INTO `dvds` (`status`, `name`, `format_id`, `type_id`, `location_id`, `rating`, `website`, `imdb`, `discs`, `episodes`, `image`, `slug`, `modified`, `created`) VALUES (1, 'asdf', 2, 4, 2, 0, '', '', NULL, NULL, 'img/dvds/23-03-2010-154416 - dvdlogoylbm.jpg', 'asdf', '2010-03-23 15:44:16', '2010-03-23 15:44:16')
whats going wrong ?
wonz (23/03/2010 - 06:22)
Warning (512): SQL Error: 1048: Column 'discs' cannot be null [CORE/libs/model/datasources/dbo_source.php, line 525]
if i doesn't choose tv-show.
James (23/03/2010 - 10:03)
@wonz: either try adding the disc value before saving or put a default value of '' into the database so that empty fields are not required.
Tomas (07/06/2010 - 12:25)
Hey James, very nice blog. Thanks a lot!
One notice thou, you are checking errors for image upload before you checked errors for inserting new dvd. In that case image will be uploaded even when dvd insert failed due to some errors, let say due to existing slug.
I just started learning cake, maybe I'm missing something.
Anyway, this is excellent tutorial. Thanks again.