Upload file in CakePHP using Media Plugin

There are many solutions to handle file upload in PHP, one of the common solutions is to create a custom action that will check the type of the file, size, validate whether the file name already exists or not, and the last thing is moving the file using move_uploaded_file(). However, it doesn’t end when the files are in your server, after that you still need to write a function to delete the files, manipulate it if needed and so on.

I already tried several ways to handle upload file in CakePHP, from writing a private action in the app_controller and the last I write a component.

I can upload file, delete file, check the file type using my Upload Component, but I realize that there are many things missing in my component.

In searching for inspiration, I found CakePHP do have serveral plugins to handle File Upload and the features are beyond my component.

One of the plugin is Media Plugin by Davidpersson.

Using the plugin is quite easy, but the wiki and the CakeFest slide is not updated with the new configuration ( I am using v1.3alpha ), so you need to jumped in to the core and trying to understand the new configuration.

Basically the plugin consist of several behaviours :

  • Transfer
  • Polymorphic
  • Meta
  • Generator
  • Coupler

But in this post I will just cover the Transfer,Coupler and Meta behaviour because I only understand these behaviours. So lets go to the implementation part, here is my CakePHP and Media Plugin version :

  1. CakePHP 1.3.0
  2. Media Plugin 1.3 alpha

First you must create a table that has a ‘file’ field in it. Remember, the field must be named ‘file’ or the plugin won’t working. Here is the example with my application :

CREATE TABLE IF NOT EXISTS `photos` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(255) NOT NULL,
  `file` varchar(255) NOT NULL,
  `dirname` varchar(255) NOT NULL,
  `basename` varchar(255) NOT NULL,
  `checksum` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=13 ;

The field that will be used by the plugin are file, dirname, basename, and checksum. But the three last fields is optional, so you can have it in your table or not (if you are using Coupler, and Meta behaviour you MUST add the there fields ). Next, bake the application ( I was using ‘cake bake’ command ).

After that grab the Media Plugin from the github and put it in the plugins folder of your application. Next you will need to include the plugin to your application, open your bootstrap.php / core.php and add this line in it :

require APP . 'plugins/media/config/core.php';

Then create the folder that will be used for storing the files. Go to your application folder in terminal and type this command ( ex: /var/www/sample ) :

cake media init

The command will ask whether you want to create the directories or not, this is the print out of my terminal :

rudy@rudy-laptop:~/www/dummy$ cake media init
---------------------------------------------------------------
Media Shell
---------------------------------------------------------------
Do you want to create missing media directories now?
[n] > y
/dummy/webroot/media/                              [OK  ]
/dummy/webroot/media/static/                       [OK  ]
/dummy/webroot/media/static/aud                    [OK  ]
/dummy/webroot/media/static/css                    [OK  ]
/dummy/webroot/media/static/doc                    [OK  ]
/dummy/webroot/media/static/gen                    [OK  ]
/dummy/webroot/media/static/ico                    [OK  ]
/dummy/webroot/media/static/img                    [OK  ]
/dummy/webroot/media/static/js                     [OK  ]
/dummy/webroot/media/static/txt                    [OK  ]
/dummy/webroot/media/static/vid                    [OK  ]
/dummy/webroot/media/transfer/                     [OK  ]
/dummy/webroot/media/transfer/aud                  [OK  ]
/dummy/webroot/media/transfer/css                  [OK  ]
/dummy/webroot/media/transfer/doc                  [OK  ]
/dummy/webroot/media/transfer/gen                  [OK  ]
/dummy/webroot/media/transfer/ico                  [OK  ]
/dummy/webroot/media/transfer/img                  [OK  ]
/dummy/webroot/media/transfer/js                   [OK  ]
/dummy/webroot/media/transfer/txt                  [OK  ]
/dummy/webroot/media/transfer/vid                  [OK  ]
/dummy/webroot/media/filter/                       [OK  ]

/dummy/webroot/media/transfer/.htaccess is not in your webroot.
Remember to set the correct permissions on transfer and filter directory.

Change the permission of the folder so that the webserver can write in it :

chown -R www-data webroot/media

Configure the ‘Photo’ model like this :

class Photo extends AppModel {
	var $name = 'Photo';
	var $displayField = 'title';
	var $actsAs = array('Media.Transfer','Media.Coupler','Media.Meta');
	var $validate = array(
		'file' => array(
			'mimeType' => array(
			'rule' => array('checkMimeType', false, array ( 'image/jpeg', 'image/png'))
		),
		'size' => array(
			'rule' => array('checkSize' , '5M')
		)
	));
}

As you can see, I added 3 behaviors ( Transfer, Coupler and Meta ). The transfer behavior is used for uploading, moving and validating the file. The coupler is for deleting the file ( it will automatically delete the file if you delete a record associated with it and also will automatically add the value in dirname and basename ), the last thing that it will do is adding the meta description to the table ( checksum ).

Edit the add.ctp and adding the ‘type’ => ‘file’ in the $this->Form->create and the $this->Form->input(‘file’) so the form can handle file upload. In the controller you don’t need to change anything because the behavior automatically handle the validation, the path and moving the file. this is my controller :

Photo->recursive = 0;
		$this->set('photos', $this->paginate());
	}

	function view($id = null) {
		if (!$id) {
			$this->Session->setFlash(sprintf(__('Invalid %s', true), 'photo'));
			$this->redirect(array('action' => 'index'));
		}
		$this->set('photo', $this->Photo->read(null, $id));
	}

	function add() {
		if (!empty($this->data)) {
			$this->Photo->create();
			if ($this->Photo->saveAll($this->data)) {
				$this->Session->setFlash(sprintf(__('The %s has been saved', true), 'photo'));
				$this->redirect(array('action' => 'index'));
			} else {
				$this->Session->setFlash(sprintf(__('The %s could not be saved. Please, try again.', true), 'photo'));
			}
		}
	}

	function edit($id = null) {
		if (!$id && empty($this->data)) {
			$this->Session->setFlash(sprintf(__('Invalid %s', true), 'photo'));
			$this->redirect(array('action' => 'index'));
		}
		if (!empty($this->data)) {
			if ($this->Photo->save($this->data)) {
				$this->Session->setFlash(sprintf(__('The %s has been saved', true), 'photo'));
				$this->redirect(array('action' => 'index'));
			} else {
				$this->Session->setFlash(sprintf(__('The %s could not be saved. Please, try again.', true), 'photo'));
			}
		}
		if (empty($this->data)) {
			$this->data = $this->Photo->read(null, $id);
		}
	}

	function delete($id = null) {
		if (!$id) {
			$this->Session->setFlash(sprintf(__('Invalid id for %s', true), 'photo'));
			$this->redirect(array('action'=>'index'));
		}
		if ($this->Photo->delete($id)) {
			$this->Session->setFlash(sprintf(__('%s deleted', true), 'Photo'));
			$this->redirect(array('action'=>'index'));
		}
		$this->Session->setFlash(sprintf(__('%s was not deleted', true), 'Photo'));
		$this->redirect(array('action' => 'index'));
	}
}
?>

and this is my add view :

Form->create('Photo',array('type' => 'file'));?>
	Form->input('title');
		echo $this->Form->input('file',array('type' => 'file'));
	?>
Form->end(__('Submit', true));?>

Now you are ready to uploading files, deleting files, and validate them in the model ( for validation you can see the wiki in the github for complete validation ). That’s all :D

14 Comments on "Upload file in CakePHP using Media Plugin"

  1. avatar beeman says:

    Thanks for this post, you got me started using this plugin!

    There was one problem, the cake media init command didn’t work with the 1.3alpha and 1.3beta version, but it did work after checking out the git master.

    Thanks!

  2. avatar Rudy says:

    @beeman Thank you for your comment, hopes it can help people who want to start using this plugin too :)

  3. avatar WyriHaximus says:

    @Rudy it is, got a link to this post via @beeman :) . Working on a rebuild of an old (sub)site of me and adding user uploads this time.

  4. avatar Rudy says:

    @WyriHaximus that’s great, be careful with the bugs :)

  5. avatar WyriHaximus says:

    @Rudy will do and going to get the max out of it. I’ll give you a poke when it’s done ;) .

  6. avatar Dave says:

    Thank you for posting this, it really helped me out, I was struggling trying to setup this plugin!

  7. avatar choen says:

    halo rudy,
    saya lagi nyari cara gimana image di webroot dapat di baca di semua halaman, ketika di input di WYSIWYG dengan cara .

    makasih sebelumnya.

  8. avatar Rudy says:

    kalau saya sih biasanya bikin tabel sendiri lagi untuk image jadi kalau user mau masukin image harus copy paste image path dari table image tersebut.

    mau buatnya seperti di wordpress ya ? saya sih blom pernah buat kalau bisa insert media langsung sih.

  9. avatar choen says:

    iya saya udah buat tabel khusus image (saya pake meioupload). yup seperti WP.

    nanya satu lagi yah. saya kan pakai slug, contoh http://domain.com/posts/judul-post
    nah kalau saya masukin alamat url yang salah misal http://domain.com/posts/judul-postxxxxx kok ngak redirect ke index atau ke 404 padahal di perintah viewnya udah ada. jadi dia kalo pake alamat salah tetep kebuka halaman postnya tapi blank kontentnya.

    satu lagi yah :) , saya banyak pake router untuk alamat tertentu. pertanyanya apakah alamat router itu nanti jadi duplicate konten dengan alamat aslinya seperti view/slug.

    makasih. oh iya boleh tau emailnya ngak sapa tau nanti saya mau nanya lagi, jadi bisa lewat email aja :) .

  10. avatar Rudy says:

    itu disetting aja di controllernya jadi kalau misalnya postsnya ga ada diredirect pakai $this->redirect();

    kalau pakai router memang bisa duplicate content, jadi misalnya saya pengen buat /posts/view/slug jadi /posts/slug dan saya setting di router maka nanti kalau user buka /posts/view/slug dan /posts/slug akan keluar halaman yang sama.

    email saya channel_life@yahoo.com.

  11. avatar Taha says:

    How can i change the upload path?…
    I will be very thankfull

  12. avatar Rudy says:

    Not sure if you can do that. Have you read the docs https://github.com/davidpersson/media/tree/next/docs ? maybe you can find something interesting in there. cheers.

  13. avatar Jeremy says:

    Nice one!

Got something to say? Go for it!