Composant Upload CakePHP (1.3) simple + miniatures

Lors du développement d’un site nous sommes souvent confrontés aux mêmes soucis. Cette fois on va aborder l’upload simple avec un composant que j’ai crée afin de gagner du temps.
Télécharger le pack complet

Que fait ce composant ?

-upload un fichier dans le répertoire de votre choix
-crée automatiquement 3 miniatures (si le fichier concerné est une image)
-stock propriétés des fichiers dans la base de données
-dispose d’une méthode de suppression de fichier en passant l’id en paramètre
-dispose d’une méthode de download de fichier en passant l’id en paramètre

Etape 1 : le base de données

Le composant va se servir d’une base de données pour stocker les liens des fichiers. Il faudra donc créer une table avec la structure suivante. Le nom de la table peut-être différent de celui proposé. (vous pourrez le changer en passant un paramètre au composant).

--
-- Structure de la table `fichiers`
--

CREATE TABLE IF NOT EXISTS `fichiers` (
`id` int(8) unsigned NOT NULL AUTO_INCREMENT,
`libelle` varchar(50) NOT NULL,
`nom_fichier` varchar(200) NOT NULL,
`url_fichier` varchar(200) NOT NULL,
`url_rep` varchar(200) NOT NULL,
`taille` int(11) NOT NULL,
`extension` varchar(50) NOT NULL,
`created` datetime DEFAULT NULL,
`modified` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=11 ;

Etape 2 : la librairie phpthumb

Nous avons besoin de la librairie phpthumb pour créer les miniatures « Télécharger phpthumb ». Ensuite dézippez le répertoire dans « /vendors/phpthumb/ »

Etape 3 : le composant

Créez un fichier « upload.php » dans le répertoire « controllers/components/ » et insérez le code suivant.
Si vous voulez savoir ce qui se passe dans le composant, je vous laisse le soin de jeter un oeil aux commentaires qui sont assez clair.

<?php
class UploadComponent extends Object
{
var $controller;

function startup(&$controller){
$this->controller = &$controller;
}

/*
*  Upload un fichier sur le serveur
* 	@param 	array ($this->data)
* 	@param	string (nom du modèle pour sauver les fichier)
* 	@param	string (rep d'upload par défaut -> webroot)
*	@param 	array (extension acceptées)
*	@param 	int (taille maximale du fichier en Mo défaut -> 2mo)
*	@param 	bool (générer automatiquement des miniatures défaut -> activé)
* 	@return	-
**/
function fichier($data,$model,$upload_rep,array $ext_valide,$taille_max_mo=2,$thumb=1){

// Stock les erreurs pour les passer à la vue
$erreurs = array();

if(!empty($data[$model]['filename']['name'])){

// Config
$err            	= 0;
$taille_max_o   	= $taille_max_mo*1048576;
$upload_rep_fichier = $upload_rep.$data[$model]['filename']['name'];
$taille_fichier     = $data[$model]['filename']['size'];
$tmp_name           = $data[$model]['filename']['tmp_name'];
$nom_fichier        = $data[$model]['filename']['name'];
$extension          = strrchr($data[$model]['filename']['name'],'.');

// On crée le répertoire de destination s'il existe pas
if(!file_exists($upload_rep)){
mkdir($upload_rep,0755,true);
}

// Type de fichier acceptés
if(!in_array($extension,$ext_valide)){
$erreurs[] = __('L\'extension du fichier n\'est pas valide.',true);
$err = 1;
}

// Taille maximal acceptée
if($taille_fichier>$taille_max_o){
$erreurs[] = __('Le fichier est trop grand.',true).' (max '.$taille_max_mo.' Mo)';
$err = 1;
}

// Si un fichier du même nom existe déjà on génère un nouveau nom
if(file_exists($upload_rep_fichier)){
$nom_fichier_sans_extension = basename($nom_fichier,$extension);
$unique_id = sha1(uniqid());
$upload_rep_fichier = $upload_rep.$nom_fichier_sans_extension.'_'.$unique_id.$extension;
$nom_fichier = $nom_fichier_sans_extension.'_'.$unique_id.$extension;
}

// Si le modèle est valide on déplace le fichier
App::Import('Model',$model);
$Fichier = new $model();

$Fichier->set($data);
$Fichier->validates();
$erreurs_models = $Fichier->invalidFields();
if(!empty($erreurs_models)){
$err = 1;
}

if($err==0){
// Si un soucis survient lors du déplacement du fichier
if(!move_uploaded_file($tmp_name,$upload_rep_fichier)){
$erreurs[] = __('Le fichier n\'a pas été transféré correctement.',true);
$err = 1;
return false;
// Si tout est en règle on déplace le fichier
}else{

$data[$model]['libelle'] = $data[$model]['libelle'];
$data[$model]['nom_fichier'] = $nom_fichier;
$data[$model]['url_fichier'] = $upload_rep_fichier;
$data[$model]['url_rep'] = $upload_rep;
$data[$model]['extension'] = $extension;
$data[$model]['taille'] = $taille_fichier;
$Fichier->save($data);

// On crée des miniatures
App::import('Vendor', 'phpthumb', array('file' => 'ThumbLib.inc.php'));
if(in_array($extension,array('.png','.jpg','.gif','.jpeg')) && $thumb==1){
// Large
if(!file_exists($upload_rep.'/thumb_small')){mkdir($upload_rep.'/thumb_small',0755);}
$thumb = PhpThumbFactory::create($upload_rep_fichier);
$thumb->resize(50,50)->save($upload_rep.'thumb_small/'.$nom_fichier);

// Medium
if(!file_exists($upload_rep.'/thumb_medium')){mkdir($upload_rep.'/thumb_medium',0755);}
$thumb = PhpThumbFactory::create($upload_rep_fichier);
$thumb->resize(100,100)->save($upload_rep.'thumb_medium/'.$nom_fichier);

// Large
if(!file_exists($upload_rep.'/thumb_large')){mkdir($upload_rep.'/thumb_large',0755);}
$thumb = PhpThumbFactory::create($upload_rep_fichier);
$thumb->resize(150,150)->save($upload_rep.'thumb_large/'.$nom_fichier);
}
return true;
}
}
// On affiche les messages d'erreur à la vue
$this->erreurs($erreurs,$erreurs_models);
}
}

/*
*  Supprime le fichier et ses miniatures
* 	@param 	string (nom du modèle)
*	@param 	array (extension)
* 	@return	-
**/
function delete($model,$conditions=null){

App::Import('Model',$model);
$Fichier = new $model();
$Fichier->recursive = 0;

if(!empty($conditions)){
$conditions = array('conditions'=>$conditions);
}

// Si on trouve le fichier
$fichier = $Fichier->find('first',$conditions);
if(!empty($fichier)){

// Supprime les fichiers physiques
// L'image principale
$f = new File($fichier[$model]['url_fichier']);
$f->delete();
// Les miniatures
$f = new File($fichier[$model]['url_rep'].'thumb_small/'.$fichier[$model]['nom_fichier']);
$f->delete();
// Supprime le rep. thumb_small si vide
if($this->TailleRep($fichier[$model]['url_rep'].'thumb_small/')==0){
$f = new Folder($fichier[$model]['url_rep'].'thumb_small/');
$f->delete();
}
$f = new File($fichier[$model]['url_rep'].'thumb_medium/'.$fichier[$model]['nom_fichier']);
$f->delete();
// Supprime le rep. thumb_medium si vide
if($this->TailleRep($fichier[$model]['url_rep'].'thumb_medium/')==0){
$f = new Folder($fichier[$model]['url_rep'].'thumb_medium/');
$f->delete();
}
$f = new File($fichier[$model]['url_rep'].'thumb_large/'.$fichier[$model]['nom_fichier']);
$f->delete();
// Supprime le rep. thumb_large si vide
if($this->TailleRep($fichier[$model]['url_rep'].'thumb_large/')==0){
$f = new Folder($fichier[$model]['url_rep'].'thumb_large/');
$f->delete();
}

// Supprime le lien vers le fichier dans la table
$Fichier->delete($fichier[$model]['id']);

return true;
}else{
return false;
}
}

/*
* Lance un téléchargement
* @param    string (nom du modèle qui stock les fichiers)
* @param    array (conditions requises pour le fichier à downloader )
* @return	bool
**/
function download($model,array $conditions=null)
{

App::Import('Model',$model);
$Fichier = new $model();
$Fichier->recursive = 0;

if(!empty($conditions)){
$conditions = array('conditions'=>$conditions);
}

// Si on trouve le fichier on lance le téléchargement
$fichier = $Fichier->find('first',$conditions);
if(!empty($fichier)){

$url_fichier = $fichier[$model]['url_fichier'];
$url_rep = $fichier[$model]['url_rep'];
$nom_fichier = $fichier[$model]['nom_fichier'];
$position = strpos($nom_fichier,'.');
$nom_fichier_s_ext = substr($nom_fichier, 0, $position);
$extension = strrchr($url_fichier,'.');

$this->controller->view = 'Media';

// Pour les "docx"
if($extension=='.docx'){
$extension = 'docx';
$params = array(
'id' => $nom_fichier,
'name' => $nom_fichier_s_ext,
'download' => true,
'extension' => 'docx',
'mimeType' => array('docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'),
'path' => APP.'webroot/'.$url_rep
);

// Pour tous les autres types de documents
}else{
$extension = substr($extension,1); // Pour avoir "zip" au lieu de ".zip"
$params = array(
'id' => $nom_fichier,
'name' => $nom_fichier_s_ext,
'download' => true,
'extension' => $extension,
'path' => APP.'webroot/'.$url_rep
);
}

$this->controller->set($params);
}
}

/*
* Prépare les erreurs à afficher dans la vue
* @param    array (liste des erreurs d'upload)
* @param    array (liste des erreurs du modèle)
* @return	-
**/
function erreurs($erreurs,$erreurs_models){
$html = '';
if(!empty($erreurs_models) || !empty($erreurs)){$html.='<strong>'.__('Les erreurs suivantes ont été détectées.',true).'</strong><br/>';}
if(!empty($erreurs_models)){
foreach($erreurs_models as $erreur_model){
$html.= '<p class="error">'.$erreur_model.'</p>';
}
}
if(!empty($erreurs)){
foreach($erreurs as $erreur){
$html.= '<p class="error">'.$erreur.'</p>';
}
}
if(!empty($err_models) || !empty($erreurs)){ $html.='<hr/>';}
$this->controller->set('erreurs',$html);
}

/*
*  Retourne la taille du répertoire
* 	@param 	string (url répertoire)
*	@param 	bool (création récursive)
* 	@return	-
**/
function TailleRep($path,$recursive=TRUE){
$result = 0;
if(!is_dir($path) || !is_readable($path))
return 0;
$fd = dir($path);
while($file = $fd->read()){
if(($file != ".") && ($file != "..")){
if(@is_dir("$path$file/"))
$result += $recursive?DirSize("$path$file/"):0;
else
$result += filesize("$path$file");
}
}
$fd->close();
return $result;
}
}
?>

Etape 4 : le modèle

On va créer un fichier « fichier.php » dans « app/models » . Et dans notre cas on demandera qu’un libellé soit indiqué au fichier.
Voici le code du modèle :

<?php
class Fichier extends AppModel{

var $name = 'Fichier';
public $validate  = array();
public function __construct(){
$this->validate = array(
'libelle'      => array(
'rule'      => 'NotEmpty',
'message'   => __('Veuillez saisir un nom pour le fichier.', true),
),

);
parent::__construct();
}
}
?>

Etape 5 : le controller

Vous trouverez juste en dessous le controller « fichier_controllers.php » au complet. Ici je vais juste présenter les différentes actions.

index :
On lit les liens des fichiers afin d’afficher une liste

// Lecture des fichiers stockés
$this->Fichier->recursive = 0;
$fichiers = $this->Fichier->find('all');
$this->set('fichiers',$fichiers);

Si on upload un fichier, les paramètres suivants sont demandés
1-$this->data
2-Nom du modèle qui stock les fichiers dans notre cas « Fichier »
3-Chemin de destination des fichiers à uploader (par défaut on se trouve dans webroot) et on décide de mettre les documents dans « upload/documents/ »
4-Les extensions de fichiers acceptées
5-Taille maximal en Mo (par défaut 2Mo)

if($this->Upload->fichier($this->data,'Fichier','upload/documents/',array('.png','.jpg','.docx','.pdf'),2)){
$this->Session->setFlash(__('Le fichier a été ajouté',true));
$this->redirect($this->referer());
}

delete:
Pour supprimer le fichier physique, son lien dans la base de donnée et ses miniatures il suffit de passer deux paramètres au composant.
1-Le nom du modèle
2-La/les conditions dans notre cas sont uniquement basées sur l’id

function delete($id){
if($this->Upload->delete('Fichier',array('Fichier.id'=>$id))){
$this->Session->setFlash(__('Le fichier a été supprimé.',true));
$this->redirect($this->referer());
}
}

download:
L’action pour lancer un téléchargement, le composant va utiliser une media view pour forcer un téléchargement.
Ici à nouveau deux paramètres comme pour delete.
1-Le nom du modèle
2-La/les conditions dans notre cas sont uniquement basées sur l’id

function download($id){
$this->Upload->download('Fichier',array('Fichier.id'=>$id));
}

Voici le fichiers_controller.php en entier.

<?php
class FichiersController extends AppController
{
var $name = 'Fichiers';
var $components = array('Upload');

/*
* Liste des fichiers / Upload
* @param 	-
* @return	-
**/
function index(){
// Initialisation des erreurs
$this->set('erreurs','');

// Lecture des fichiers stockés
$this->Fichier->recursive = 0;
$fichiers = $this->Fichier->find('all');
$this->set('fichiers',$fichiers);

// Si un fichier est uploadé
if($this->Upload->fichier($this->data,'Fichier','upload/documents/',array('.png','.jpg','.docx','.pdf'),2)){
$this->Session->setFlash(__('Le fichier a été ajouté',true));
$this->redirect($this->referer());
}
}

/*
* Page suppression des diplômes
* @param 	int (id fichier à supprimer)
* @return	-
**/
function delete($id){
if($this->Upload->delete('Fichier',array('Fichier.id'=>$id))){
$this->Session->setFlash(__('Le fichier a été supprimé.',true));
$this->redirect($this->referer());
}
}

/*
* Page de téléchargement des diploms
* @param 	int (id fichier à supprimer)
* @return	-
**/
function download($id){
$this->Upload->download('Fichier',array('Fichier.id'=>$id));
}

}
?>

Etape 6 : la vue

Ici rien de particulier, on crée un formulaire standard qui permet d’uloader un fichier.
Les fichiers uploadés sont ensuite listés en dessous avec la possiblité de les télécharger/supprimer.

<?php
echo '<hr/>';
echo $erreurs;
echo $form->create('Fichier',array('type' => 'file','url' => '/fichiers/index'));
echo $form->input('libelle', array('type' => 'text','label'=>__('Nom du fichier',true)));
echo $form->input('filename', array('type' => 'file','label'=>__('Sélectionnez un fichier au format',true).' (.png,.jpg,.docx,.pdf)'));
echo '<br/><hr/>';
echo $this->Form->submit(__('Ajouter',true)).'</div>';
echo $form->end();

echo '<h2>'.__('Liste des fichiers',true).'</h2>';
echo '<hr/>';
if(!empty($fichiers)){
echo '<table>';
foreach($fichiers as $fichier){

$id = $fichier['Fichier']['id'];
$libelle = $fichier['Fichier']['libelle'];
$nom_fichier = $fichier['Fichier']['nom_fichier'];
$fichier = $fichier['Fichier']['url_fichier'];

echo '<tr>';
echo '<td>'.$libelle.' ( '.$nom_fichier.' )</td>';
echo '<td>'.$html->link(__('Supprimer', true), array('controller' => 'fichiers', 'action' => 'delete/',$id),array(),__('Etes-vous sûr ?',true)).'</td>';
echo '<td>'.$html->link(__('Télécharger', true), array('controller' => 'fichiers', 'action' => 'download/'.$id)).'</td>';
echo '</tr>';

}
echo '</table>';
}else{
echo '- '.__('Aucun élément',true);
}
echo '<hr/>';
?>

Autres

Si vous voulez customizer l’apparence du message d’erreur vous pouvez modifier la fonction « erreurs » du composant.

6 commentaires sur “Composant Upload CakePHP (1.3) simple + miniatures”

  1. Bonjour,

    j’utilise votre fonction, ça marche bien excepté pour les thumbnails, si je leur précise une taille différente de celle par défaut, ils gardent quand même la valeur par défaut, j’ai beau cherché je vois pas ou c’est écrit en dur dans le code.

    Merci par avance 🙂

  2. salut a tous, merci pour ce super tuto, ca marchait tres bien pour les fotos par contre pour les videos ca marche plus, pouvez vous m’aider ? et merci d’avance

  3. Bonjour et merci pour ce tuto mais lors de mon passage à cakephp 2 jai une erreur au niveau de la class File. Une personne aurai-t-elle trouvé une solution ?

    D’avance, merci

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *