Hello, j'ai voulu installé le Plugin d'Upload de Xeta, j'ai donc DL le plugin et l'est installer dans plugins, et j'ai une erreur absurde !

*Xety/Cake3Upload.UploadBehavior could not be found.aroundaroundMake sure your plugin Xety/Cake3Upload is in the /var/www/local.dev/jobcarer/plugins/ directory and was loaded.*
*Error: Create the class UploadBehavior below in file: /var/www/local.dev/jobcarer/plugins/Xety/Cake3Upload/src/Model/Behavior/UploadBehavior.php*

47 réponses


JC_Pires
Réponse acceptée

Okay c'est que depuis la 3.0 il faut mettre un _initialize dans les behaviors donc va dans ton dossier

vendor\xety\cake3-upload\src\Model\Behavior\UploadBehavior.php

Et avant la fonction beforeSave()

met ça:

    public function initialize(array $config)
    {

       $this->_config = array_merge ($this->_defaultConfig , $config);
    }

Après dans ton formulaire:

    <?= $this->Form->create('Topics', ['type' => 'file']);?>
    ...
     <?= $this->Form->input('thumbnails_file', ['type' => 'file','class' => 'form-control']) ?>
     <?= $this->Form->button('Creer', ['type' => 'submit', 'class' => 'btn btn-primary']);?>
     <?= $this->Form->end();?>

dans ton controller juste avant ton save:

    $topic->thumbnails_file = $this->request->data['thumbnails_file'];

Chez moi ça marche niquel, il manquais la fonction d'initialize au behavior, en espérant que ça marche chez toi, je soumettrais ma modif a Xeta sur github

Et tu l'as bien load ?

Muxabble
Auteur

Ba j'ai utilisé

Plugin::loadAll();

pour être sur et j'ai même essayé

Plugin::load('Xety\Cake3Upload);

rien à faire

Mets le dans tes dépendences de composer et puis composer Update , il va te l'installer dans le dossier Vendor et tu n'auras qu'a le load après dans ton bootstrap.php

Exemple de mon composer.json

{
    "name": "cakephp/app",
    "description": "CakePHP skeleton app",
    "homepage": "http://cakephp.org",
    "type": "project",
    "license": "MIT",
    "require": {
        "php": ">=5.4.16",
        "cakephp/cakephp": "~3.0",
        "mobiledetect/mobiledetectlib": "2.*",
        "cakephp/migrations": "@stable",
         "ezyang/htmlpurifier": "dev-master",
        "cakephp/plugin-installer": "*",
        "xety/cake3-sluggable": "1.*",
        "xety/cake3-upload": "1.*",
        "xety/cake3-cookieauth": "1.*"
    },
    "require-dev": {
        "psy/psysh": "@stable",
        "cakephp/debug_kit": "~3.0",
        "cakephp/bake": "~1.0"
    },
    "suggest": {
        "phpunit/phpunit": "Allows automated tests to be run without system-wide install.",
        "cakephp/cakephp-codesniffer": "Allows to check the code against the coding standards used in CakePHP."
    },
    "autoload": {
        "psr-4": {
            "App\\": "src"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "App\\Test\\": "tests",
            "Cake\\Test\\": "./vendor/cakephp/cakephp/tests"
        }
    },
    "scripts": {
        "post-install-cmd": "App\\Console\\Installer::postInstall",
        "post-autoload-dump": "Cake\\Composer\\Installer\\PluginInstaller::postAutoloadDump"
    },
    "minimum-stability": "dev",
    "prefer-stable": true
}
Muxabble
Auteur

Ah oui non je ne peux pas utiliser composer, il trouve toujours des erreurs du coup ba je ne l'utilise pas !

Ah dommage. Il faut vraiment que tu vois à ça car c'est incontournable de nos jours.

Est-ce que le plugin est bien dans le dossier plugin sous cette forme Tondossier/plugins/xety/cake3-upload

Où a tu pris la version que tu as installée 'github j'imagine' et comment?

Si tu l'as pris en .zip par exemple, le dossier ressemble à ceci cake3-upload-master si tu as copié/collé ce dossier tel quel dans ton dossier plugins ça n'ira pas il te faut bien le mettre dans un dossier xety et renommer ton dossier cake3-upload-master en cake3-upload à l'intérieur du dossier plugins/xety. En tout cas c'est comme ça qu'il est dans le dossier vendor. C'est tellement plus simple avec composer qu'il faut que tu corriges ton problème.

En espérant t'aider.

Muxabble
Auteur

Oui j'ai bien renomé le dossier et j'ai enfin pris le temps de voir ce foutu soucis de Composer ! En fait vu que j'avais tester Symfony ba j'avais instalelr NFS, et apparement il y aurais des bugs avec ce module du coup j'ai refais une VM sans NFS x)

Bon du coup tu devrais pas avoir trop de problème pour l'installer maintenant. Sinon ce n'est qu'une simple classe "Behavior". Tu pourrais juste déplacer le fichier dans le bon dossier dans ton dossier src/Model/Behavior, changer le namespace et hop ça fonctionnera tout aussi bien.

Dossier: src/Model/Behavior
Namespace: App\Model\Behavior

Après t'adaptes juste pour l'attacher à tes models, par exemple

  $this->addBehavior('Upload', [
            'fields' => [
                'avatar' => [
                    'path' => 'upload/avatar/:id/:md5'
                ]
            ]
        ]
    );
Muxabble
Auteur

Alors je viens de l'installer avec composer sauf que je n'ai rien dans mon fichier plugins .. bon je vais essayer de le mettre dans le model du coup ! le soucis c'est est-ce adaptable pour upload des images à la une par exemple ?!

Lors d'une installation avec Composer toutes tes dépendences s'installeront dans ton dossier vendor, je fais toujours apres un: composer dumpautoload pour être sûr que toutes les classes soient prise en compte dans l'autoloader.

Pour l'avoir testé et l'utiliser dans mon projet de CMS Blog/Forum, ce behavior te permet de sauter tout la partie enregistrement des fichiers envoyé dans tes formulaires juste en spécifiant le nom de l'input d'une certaine manière, manière que tu peut redéfinir lors de l'attachement du Behavior à ton Model.

Dans un formulaire d'une vue, exemple la vue se trouvant dans src/Templates/Topics/add.ctp:

    <?= $this->Form->input('thumbnails_file', ['type' => 'file']) ?>

Dansl le Model, exemple le model se trouvant dans src/Model/Table/TopicsTable.php:

    <?php
    namespace App\Model\Table;

    use Cake\ORM\Table;
    use Cake\ORM\Query;
    use Cake\Validation\Validator;

    class TopicsTable extends Table
    {
        public function initialize(array $config)
        {
              $this->table('topics');
              $this->displayField('id');
              $this->primaryKey('id');
              $this->addBehavior('Xety/Cake3Upload.Upload', [
                              'fields' => [
                                  'thumbnails' => [
                                      'path' => '/img/articles/:y/:m/:md5',
                                      'overwrite' => true,
                                  ]
                              ]
                          ]
                      );
          }
    }

Tu as la possibilité de redéfinir le champ où tu veut que soit stocké l'url du fichier téléchargé, dans mon exemple c'est "thumbnails" dans ma Table Topics, par la suite il te suffit d'ajouter "_file" à ton input comme ci-haut pour déclencher le behavior : "nomdechamp_file". Tu peut également redéfinir le path ou il sera enregistré, également la récurrence si "overwrite => true ", il supprimera le fichier lors de la suppression de l'enregistrement en BDD également.

Je tiens à préciser que ce behavior fonctionne parfaitement, comme les autres d'ailleurs. Merci encore à Xeta.

Muxabble
Auteur

Je te crois, j'en suis même sur, le soucis c'est qu'il ne m'enregistre rien en BDD ! ni rien le path en ajoutant thumbnail_file ...

Est-ce que le fichier s'enregistre quand meme dans ton dossier webroot?

Muxabble
Auteur

Non même pas ...

Donne nous les champs de ta table où tu veut que ça soit enregistré stp.

Muxabble
Auteur

Ba thumbnail => varchar 255

Fais un composer dumpautoload, j'ai l'impression que la Classe n'est pas chargée.

Sinon donne nous le formulaire, ainsi que ton model stp

Muxabble
Auteur

Je n'avais pas fais le composer dumpautoload ! Mais toujours rien ...

Je prend la vue edit.ctp

<?= $this->Form->input('thumbnail_file', ['type' => 'file']) ?>
<?php
namespace App\Model\Table;

use App\Model\Entity\Post;
use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;

/**
 * Posts Model
 */
class PostsTable extends Table
{

    /**
     * Initialize method
     *
     * @param array $config The configuration for the Table.
     * @return void
     */
    public function initialize(array $config)
    {
        $this->table('posts');
        $this->displayField('name');
        $this->primaryKey('id');
        $this->addBehavior('Timestamp');
        $this->addBehavior('Sluggable');
        $this->addBehavior('Xety/Cake3Upload.Upload', [
                'fields' => [
                    'thumbnail' => [
                        'path' => '/img/articles/:y/:m/:md5',
                        'overwrite' => true,
                    ]
                ]
            ]
        );
        $this->addBehavior('CounterCache', ['Categories' => ['post_count']]);
        $this->belongsTo('Users', [
            'foreignKey' => 'user_id',
            'joinType' => 'INNER'
        ]);
        $this->belongsTo('Categories', [
            'foreignKey' => 'category_id',
            'joinType' => 'INNER'
        ]);
    }

    /**
     * Default validation rules.
     *
     * @param \Cake\Validation\Validator $validator Validator instance.
     * @return \Cake\Validation\Validator
     */
    public function validationDefault(Validator $validator)
    {
        $validator
            ->add('id', 'valid', ['rule' => 'numeric'])
            ->allowEmpty('id', 'create')
            ->requirePresence('name', 'create')
            ->notEmpty('name')
            ->requirePresence('excerpt', 'create')
            ->notEmpty('excerpt')
            ->requirePresence('content', 'create')
            ->notEmpty('content')
            ->add('published', 'valid', ['rule' => 'boolean'])
            ->requirePresence('published', 'create')
            ->notEmpty('published');

        return $validator;
    }

    /**
     * Returns a rules checker object that will be used for validating
     * application integrity.
     *
     * @param \Cake\ORM\RulesChecker $rules The rules object to be modified.
     * @return \Cake\ORM\RulesChecker
     */
    public function buildRules(RulesChecker $rules)
    {
        $rules->add($rules->existsIn(['user_id'], 'Users'));
        $rules->add($rules->existsIn(['category_id'], 'Categories'));
        return $rules;
    }
}

Si tu as créé une Entity pour ta table et que tu as un protected $_accessible il faut ajouter 'thumbnail_file'=>true sinon il ignore tous simplement le champ thumbnail_file du coup il enregistre ni le fichier et ni le chemin vers l'image dans la base de données.

Je viens d'avoir un problème équivalent au tiens quand j'ai installe le plugin et cela à régler le problème.

Muxabble
Auteur

@Assassins, pas bête après rélfexion voir même logique, mais le model du behavior devrait s'en occuper, mais même avec sa, il ne veut toujours pas ...

L'enregistrement de l'entité ce fais?

Muxabble
Auteur

Non rien dans la base de donnée, rien dans img/upload

// PostsController.php
public function edit($id = null)
    {
        $post = $this->Posts->get($id, [
            'contain' => []
        ]);
        if ($this->request->is(['patch', 'post', 'put'])) {
            $post = $this->Posts->patchEntity($post, $this->request->data);
            if ($this->Posts->save($post)) {
                $this->Flash->success("L'article à bien été sauvegardé.");
                return $this->redirect(['action' => 'index']);
            } else {
                $this->Flash->error("L'article n'as pas pu être sauvegardé. Merci d'essayer de nouveau.");
            }
        }
        $users = $this->Posts->Users->find('list', ['limit' => 200]);
        $categories = $this->Posts->Categories->find('list', ['limit' => 200]);
        $this->set(compact('post', 'users', 'categories'));
        $this->set('_serialize', ['post']);
    }

Donc le problème viens de là. Après avoir fais patchEntity, debug ta variable $post et regarde si tout les champ requis pour la validation sont bien présent dans l'objet, sinon redéfinis les $post->tonchamp = ...

Si thumbnail est pas autorisé à être null en bdd ça pourrais déclencher une erreur aussi. Mais je doute que ça soit le cas ?!

Tu as un problème au if($this->Posts->save($post)){} soit tu rentres pas dans la condition if ($this->request->is(['patch', 'post', 'put'])) ce qui m'étonnerais fortement, sinon c'est des champ qui ne sont pas définis après le patchEntity ou la requête get() qui ne trouve aucun enregistrement mais j'en doute encore plus.

Muxabble
Auteur

Okey ! J'ai réussi pour l'enregistrement en BDD mais il ne l'upload pas ...

$post->thumbnail = $post->thumbnail_file;

Ok bon premier pas, tu n'as pas a faire ça:

$post->thumbnail = $post->thumbnail_file;

C'est le behavior qui va s'en charger.

Dans ton create() tu lui a bien indiqué qu'il faut qu'il ajoute "enctype="multipart/form-data" en faisant:

Pour l'edition:

    <?= $this->Form->create($topic, ['type' => 'file']);?>

Pour un ajout:

    <?= $this->Form->create('Topics', ['type' => 'file']);?>
Muxabble
Auteur

Arf, les formulaires d'upload ne sont pas comme d'hab c'est vrai, j'avais complétement zappé ! Donc du coup j'ai enlevé le $post->thumbnail = $post->thumbnail_file; mais ducoup plus d'enregistrement en bdd et pas d'upload non plus

Ca veut dire que ton champ n'autorise pas à être null dans ta base de données et ou que tu as un problème de validation des données. Commente la partie validation de ton model et test. Vérifie bien le null autorisé dans ta base de données pour le champ thumbnail au passage.

Muxabble
Auteur

Alors j'ai passé thumbnail en Null Yes dans ma bdd, et j'ai tester avec et sans la validation, il n'update pas dans la bdd, et il n'upload toujours pas !
J'ai testé à l'ajout, il considère thumbnail_file comme vide !

Envoi tout ton formulaire stp

Ah je n'avais pas retesté avec ma version à jour de cake 3, et j'ai le même problème ... Je l'avais testé avec une dev preview 2 semaines avant la 3.0

Ok j'ai trouvé le problème, il ne viens pas de nous mais du behavior, je suis entrain d'effectuer les modifs, je te redis et j'en parle a Xeta

Muxabble
Auteur

Okey pas de soucis x) Je vais y jette un coup d'oeil !

Dans ton model

    $this->addBehavior('Xety/Cake3Upload.Upload', [
                'fields' => [
                    'thumbnails' => [
                        'path' => '/img/articles/:y/:m/:md5',
                        'overwrite' => true,
                    ]
                ]
            ]
        );
Muxabble
Auteur

Merci pour cette contribution, en effet c'était le behavior, maintenant tout rentre dans l'ordre, un p'tit bémol en bdd il enregistre /img/:file/:y/:y/:md5, le truc c'est que pour récuperer l'image avec le Helper Html, le helper génère

<img ... /img/img/articles/2015/04/ghji8.jpg>

Chez moi le behavior fontionne très bien sans modification, et ce même avec la version 3.0.1.

Oui Xeta à raison, désolé mais effectivement même sans la function initialize() le Behavior fonctionne. Je trouvais bizarre justement que ça vienne du Behavior, mais il ne restais plus beaucoup de solution, et depuis que je l'avais testé des choses avais changée dans le framework.

La seule chose que je dois faire c'est dans mon controller avant mon save c'est:

    $topic->thumbnails_file = $this->request->data['thumbnails_file'];

Chose que je n'étais pas obligé de faire avant.

Désolé d'avoir douté @Xeta.

En ce qui concerne ton problème @dubleYu concernant le chemin enregistré en base de données, tu as juste a changer le chemin dans ton addBehavior, tout simplement:

    $this->addBehavior('Xety/Cake3Upload.Upload', [
                'fields' => [
                    'thumbnails' => [
                        'path' => 'articles/:y/:m/:md5', // enleve le /img
                        'overwrite' => true,
                    ]
                ]
            ]
        );
Muxabble
Auteur

C'est ce que j'ai fais, mais du coup il me l'enregistre dans le dossier webroot et non plus dans img ...

essai ça "Pas testé"

    $this->addBehavior('Xety/Cake3Upload.Upload', [
                'fields' => [
                    'thumbnails' => [

                        'path' => 'articles/:y/:m/:md5', 
                        'overwrite' => true,
                    ]
                ],
                'root' => IMAGES // le root de base sera plus webroot mais le dossier image
            ]
        );
Muxabble
Auteur

J'ai trouvé une alternative

'path' => 'img/upload/articles/:y/:m/:md5',

Voilà dsl la constante IMAGES n'existe plus dans cake3

    $this->addBehavior('Xety/Cake3Upload.Upload', [
                'fields' => [
                    'thumbnails' => [

                        'path' => 'articles/:y/:m/:md5', 
                        'overwrite' => true,
                    ]
                ],
                'root' => WWW_ROOT. DS . 'img'.DS
            ]
        );
Muxabble
Auteur

Merci pour l'info x)

A la place de:

$topic->thumbnails_file = $this->request->data['thumbnails_file'];

test ça :
https://github.com/Xety/Xeta/blob/master/src/Controller/UsersController.php#L158

Ca veut pas. Il ne passe pas dans le tableau $data dans le behavior, il coupe l'évènement ici :

if (!isset($data[$virtualField]) || !is_array($data[$virtualField])) {

Si il ne passe pas, alors sa viens probablement des règles de validations ou des Rules. Si chez moi sa fonctionne il y aucune raison que ça fonctionne pas chez toi. ^^
https://github.com/Xety/Xeta/blob/master/src/Model/Table/UsersTable.php#L151-L181

Muxabble
Auteur

Je vais check tout sa merci x)

J'ai essayé avec les modifications des validations, sans validation. Avec accessible/sans mais le seul moyen pour que ça fonctionne c'est:

    $topic->thumbnails_file = $this->request->data['thumbnails_file'];

Alors j'aimerais vraiment comprendre pq ça ne veut pas, et je précise qu'avant la 3.0.0 ou la 3.0.1 je n'avais pas ce problème tout fonctionnais normalement.

    //Mon controller TopicsController

    public function add()
    {
        $topic = $this->Topics->newEntity($this->request->data);
        if ($this->request->is('post')) {           
            $topic->accessible('thumbnails_file', true); 
            $article->user_id = $this->Auth->user('id');
            $topic->thumbnails_file = $this->request->data['thumbnails'];

            if ($this->Topics->save($topic)) { 
            ... 
            }
    }
    //Ma vue:    
        <?= $this->Form->create($topic, ['type' => 'file']);?>   
        <?= $this->Form->input('thumbnails', ['type' => 'file','class' => 'form-control']) ?>
        <?= $this->Form->button('Creer', ['type' => 'submit', 'class' => 'btn btn-primary']);?>
        <?= $this->Form->end();?>
     // Mon model

    <?php
    namespace App\Model\Table;

    use App\Model\Entity\Topic;
    use Cake\ORM\Query;
    use Cake\ORM\RulesChecker;
    use Cake\ORM\Table;
    use Cake\Validation\Validator;

    /**
     * Topics Model
     */
    class TopicsTable extends Table
    {

    /**
     * Initialize method
     *
     * @param array $config The configuration for the Table.
     * @return void
     */
    public function initialize(array $config)
    {
        $this->table('topics');
        $this->displayField('title');
        $this->primaryKey('id');
        $this->addBehavior('Timestamp');
        $this->addBehavior('CounterCache', ['Forums' => ['topic_count'], 'Users'=> ['topic_count']]);

        $this->addBehavior('Xety/Cake3Sluggable.Sluggable');
        $this->addBehavior('Xety/Cake3Upload.Upload', [
                'fields' => [
                    'thumbnails' => [
                        'path' => 'articles/:y/:m/:md5',
                        'overwrite' => true,
                    ]
                ],
                'root' => WWW_ROOT. DS . 'img'.DS
            ]
        );

        $this->belongsTo('ParentForums', [
             'className' => 'Forums',
            'foreignKey' => 'forum_id'
        ]);

        $this->belongsTo('Forums', [
            'foreignKey' => 'forum_id'
        ]);

        $this->belongsTo('Users', [
            'foreignKey' => 'user_id'
        ]);
        $this->belongsTo('LastUser', [
             'className' => 'Users',
            'foreignKey' => 'lastuser'
        ]);
        $this->belongsTo('Groups', [
            'foreignKey' => 'group_id'
        ]);
        $this->hasMany('Posts', [
            'foreignKey' => 'topic_id'
        ]);

        $this->hasMany('TopicsUsersTrackers', [
            'foreignKey' => 'topic_id'
        ]);
        $this->hasMany('RatingsUsers', [
            'className' => 'RatingsUsers',
            'foreignKey' => 'refid',
            'conditions' => ['ref' => 'topic']
        ]);
    }

    public function findShort(Query $query) {
            return $query
                ->select([
                    'id', 'title', 'slug', 'ParentForums.slug'
                ])
                ->contain(
                    [
                        'ParentForums' => function ($q){
                            return $q
                                ->find('short');
                        }
                    ]
                );
    }
    public function findListe(Query $query, $options) {
            return $query
                ->contain(
                    [
                        'Users' => function ($q){
                            return $q
                                ->find('short');
                        },
                        'LastUser' => function ($q){
                            return $q
                                ->find('short');
                        },
                        'TopicsUsersTrackers'
                    ]
                )
                ->order(['Topics.modified' => 'DESC']);
        }

      /**
       * Default validation rules.
       *
       * @param \Cake\Validation\Validator $validator Validator instance.
       * @return \Cake\Validation\Validator
       */
    public function validationDefault(Validator $validator)
      {
          $validator
              ->add('id', 'valid', ['rule' => 'numeric'])
              ->allowEmpty('id', 'create')
              ->add('is_article', 'valid', ['rule' => 'boolean'])
              ->requirePresence('is_article', 'create')
              ->notEmpty('is_article')
              ->add('is_pinned', 'valid', ['rule' => 'boolean'])
              ->requirePresence('is_pinned', 'create')
              ->notEmpty('is_pinned')
              ->add('is_private', 'valid', ['rule' => 'boolean'])
              ->requirePresence('is_private', 'create')
              ->notEmpty('is_private')
              ->requirePresence('title', 'create')
              ->notEmpty('title')
              ->requirePresence('content', 'create')
              ->notEmpty('content')
              ->add('commentable', 'valid', ['rule' => 'boolean'])
              ->requirePresence('commentable', 'create')
              ->notEmpty('commentable')

              // Ajout de tes règles                
              ->allowEmpty('thumbnails_file')
              ->add('thumbnails_file', [
                  'mimeType' => [
                      'rule' => ['mimeType', ['image/jpeg', 'image/png']],
                      'message' => __("The mimeType is not allowed."),
                      'on' => function ($context) {
                              return !empty($context['data']['thumbnails_file']['name']);
                      }
                  ],
                  'fileExtension' => [
                      'rule' => ['extension', ['jpg', 'jpeg', 'png']],
                      'message' => __("The extensions allowed are {0}.", '.jpg, .jpeg and .png'),
                      'on' => function ($context) {
                              return !empty($context['data']['thumbnails_file']['name']);
                      }
                  ],
                  'fileSize' => [
                      'rule' => ['fileSize', '<', '500KB'],
                      'message' => __("The file exceeded the max allowed size of {0}", '500KB'),
                      'on' => function ($context) {
                              return !empty($context['data']['thumbnails_file']['name']);
                      }
                  ],
              ]);

          return $validator;
      }
      /**
       * Returns a rules checker object that will be used for validating
       * application integrity.
       *
       * @param \Cake\ORM\RulesChecker $rules The rules object to be modified.
       * @return \Cake\ORM\RulesChecker
       */
      public function buildRules(RulesChecker $rules)
      {
          $rules->add($rules->existsIn(['forum_id'], 'Forums'));
          $rules->add($rules->existsIn(['user_id'], 'Users'));
          $rules->add($rules->existsIn(['group_id'], 'Groups'));
          return $rules;
      }
    }

Si tu vois quelque chose Xeta, car là je pêche ...

Remplace :

<?= $this->Form->input('thumbnails', ['type' => 'file','class' => 'form-control']) ?>

par

<?= $this->Form->input('thumbnails_file', ['type' => 'file','class' => 'form-control']) ?>

C'est marqué dans la doc du plugin : https://github.com/Xety/Cake3-Upload#identifiers

Oui oui excuse moi, j'ai juste changé avant pour un test mais c'est bien:

    <?= $this->Form->input('thumbnails_file', ['type' => 'file']) ?>