SonataMediaBundle - comment télécharger des images?

devrait probablement être intitulé: "SonataMediaBundle-où est le howto manquant?".

j'ai fait quelques backend admin avec sonataAdminBundle et sonataDoctrineORMAdminBundle (et d'autres), la plupart des choses ont fonctionné comme prévu, mais j'ai laissé le téléchargement de fichier et la manipulation pour plus tard, parce que je pensais "à quel point cela peut être difficile?".

pour faire court - est-ce qu'il y a de la documentation sur les choses les plus simples-c. - à-d. avoir des images attachées à un poteau ou à une entrée, comment configurer la classe admin sonata, comment afficher les pouces de l'image sous forme d'édition,etc.?

première page de documentation se termine par "vous pouvez visiter votre tableau de bord admin" comme si je pouvais m'attendre à des changements pertinents là-bas, peut-être media manager opérationnel, ou quelque chose. Mais ce n'est pas le cas.

la page suivante traite brièvement des héplers, puis une autre page avec une étude de cas assez compliquée du fournisseur vimeo.

j'ai cherché partout sur le web et le mieux que j'ai pu trouver, était champ de téléchargement avec popup ajax, et la liste des fichiers téléchargés.

Dans mon admin classe j'ai:

protected function configureFormFields(FormMapper $formMapper)
{
    $formMapper
    ->with('general')
        ->add('title')
        ->add('body')
        ->add('categories')
        ->end()
    ->with('media')
        ->add('images', 'sonata_type_model')

dans ma classe News:

/**
 * @ORMManyToMany(targetEntity="ApplicationSonataMediaBundleEntityMedia")
 */
public $images; 

et toutes les configurations et routines yaml sont implémentées.

Le résultat est: Fatal error: Call to a member function add() on a non-object in [some-entity].php lorsque vous essayez de télécharger une image, et sélectionnez la liste des id d'image avec le signe "plus" (Champ sonata_type_model je suppose).

je suis coincé. J'ai été en mesure de créer des médias "manager "juste en sf2 simple dans une heure ou deux, mais c'était un autre projet et réécrire un courant à ce modèle signifie de commencer"à partir de zéro". Alors-que faire pour que sonataMediaBundle et sonataAdminBundle fonctionnent comme prévu?


EDIT: voici ce que j'ai fait à la place:

Ma classe news (ou tout autre qui a besoin de transfert d'image):

<?php

namespace SomeSiteBundleEntity;

use DoctrineORMMapping as ORM;
use DoctrineCommonCollectionsArrayCollection;
use SymfonyComponentValidatorConstraints as Assert;


/**
 * SomeSiteBundleEntityNews
 *
 * @ORMTable(name="news")
 */
class News
{
    /**
     * @var integer $id
     *
     * @ORMColumn(name="id", type="integer")
     * @ORMId
     * @ORMGeneratedValue(strategy="AUTO")
     */
    protected $id;      


    //some stuff...

  /**
    * @var Document documents
     * @ORMManyToMany(targetEntity="Document", cascade={"persist", "remove", "delete"} )
     **/
    protected $documents;


    public function __construct()
  {
    $this->documents = new ArrayCollection();

  }

[...]

    /**
     * Add documents
     *
     * @param FestusSiteBundleEntityDocument $documents
     */
    public function addDocument(FestusSiteBundleEntityDocument $document)
    {
        $this->documents[] = $document;
    }

    /**
     * set document
     *
     * @param FestusSiteBundleEntityDocument $documents
     */
    public function setDocument(FestusSiteBundleEntityDocument $document)
    {
        foreach ($this->documents as $doc) {
            $this->documents->removeElement($doc);
        }
        $this->documents[] = $document;
    }

    /**
     * Get documents
     *
     * @return DoctrineCommonCollectionsCollection 
     */
    public function getDocuments()
    {
        return $this->documents;
    }



    // setters, getters...

ma classe de document (nécessaire pour changer le nom de la table, parce que je suis tombé sur problèmes avec les mots réservés sur certains serveurs):

<?php  

namespace SomeSiteBundleEntity;

use DoctrineORMMapping as ORM;
use DoctrineCommonCollectionsArrayCollection;
use SymfonyComponentValidatorConstraints as Assert;

/**
 * SomeSiteBundleEntityDocument
 *
 * @ORMTable(name="docs")
 * @ORMEntity
 * @ORMHasLifecycleCallbacks
 */
class Document
{
    /**
     * @ORMId
     * @ORMColumn(type="integer")
     * @ORMGeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @ORMColumn(type="string", length=255)
     * @AssertNotBlank
     */
    private $name;

    /**
     * @ORMColumn(type="string", length=255, nullable=true)
     */
    private $path;

    /**
     * @AssertFile(maxSize="6000000")
     */
    private $theFile;


    /**
     * @ORMColumn(type="datetime", name="created_at")
     * 
     * @var DateTime $createdAt
     */
    protected $createdAt;


    /**
     * @ORMColumn(type="integer")
     */
    private $type = 1;


    public function __construct()
      {
        $this->createdAt = new DateTime();
      }

    public function getAbsolutePath()
    {
        return null === $this->path ? null : $this->getUploadRootDir().'/'.$this->path;
    }

    public function getWebPath()
    {
        return null === $this->path ? null : $this->getUploadDir().'/'.$this->path;
    }

    protected function getUploadRootDir()
    {
        // the absolute directory path where uploaded documents should be saved
        return __DIR__.'/../../../../web/'.$this->getUploadDir();
    }

    protected function getUploadDir()
    {
        // get rid of the __DIR__ so it doesn't screw when displaying uploaded doc/image in the view.
        return 'uploads/documents';
    }   

    /**
     * @ORMPrePersist()
     * @ORMPreUpdate()
     */
    public function preUpload()
    {
        if (null !== $this->theFile) {
            //var_dump($this);
            // do whatever you want to generate a unique name
            $this->path = uniqid().'.'.$this->theFile->guessExtension();
        }
    }

    /**
     * @ORMPostPersist()
     * @ORMPostUpdate()
     */
    public function upload()
    {
        if (null === $this->theFile) {
            return;
        }

        // if there is an error when moving the file, an exception will
        // be automatically thrown by move(). This will properly prevent
        // the entity from being persisted to the database on error
        $this->theFile->move($this->getUploadRootDir(), $this->path);

        unset($this->theFile);
    }

    /**
     * @ORMPostRemove()
     */
    public function removeUpload()
    {
        if ($file = $this->getAbsolutePath()) {
            unlink($file);
        }
    }

    public function __toString()
      {
        return 'Document';
      }


    /**
     * Get id
     *
     * @return integer 
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set name
     *
     * @param string $name
     */
    public function setName($name)
    {
        $this->name = $name;
    }

    /**
     * Get name
     *
     * @return string 
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Set file
     *
     * @param string $file
     */
    public function setTheFile($file)
    {
        $this->theFile = $file;
    }

        /**
     * Get file
     *
     * @return string 
     */
    public function getTheFile()
    {
        return $this->theFile;
    }

    /**
     * Set path
     *
     * @param string $path
     */
    public function setPath($path)
    {
        $this->path = $path;
    }

    /**
     * Get path
     *
     * @return string 
     */
    public function getPath()
    {
        return $this->path;
    }


    /**
     * Set type
     *
     * @param string $type
     */
    public function setType($type)
    {
        $this->type = $type;
    }

    /**
     * Get type
     *
     * @return string 
     */
    public function getType()
    {
        return $this->type;
    }


    /**
     * Gets an object representing the date and time the user was created.
     * 
     * @return DateTime A DateTime object
     */
    public function getCreatedAt()
    {
        return $this->createdAt;
    }


     /**
     * Gets an object representing the date and time the user was created.
     * 
     * @return DateTime A DateTime object
     */
    public function getCreatedAtString()
    {
        return date_format($this->createdAt, "Y-m-d");
    }


    /**
     * Set createdAt
     *
     * @param datetime $createdAt
     */
    public function setCreatedAt($createdAt)
    {
        $this->createdAt = $createdAt;
    }


}

comme vous pouvez le voir, la plupart sont copiés à partir du tutoriel de symfony2.

Maintenant, pour le contrôleur:

<?php 

namespace SomeSiteBundle;

use SomeSiteBundleFormTypeImageShowType;
use SomeSiteBundleEntityDocument;
use SonataAdminBundleAdminAdmin;
use SonataAdminBundleDatagridListMapper;
use SonataAdminBundleDatagridDatagridMapper;
use SonataAdminBundleValidatorErrorElement;
use SonataAdminBundleFormFormMapper;
use SonataAdminBundleShowShowMapper;
use SymfonyComponentHttpFoundationFileFile;
use SymfonyComponentHttpFoundationRequest;


class NewsAdmin extends Admin
{

    public function __construct($code, $class, $baseControllerName) {
        parent::__construct($code, $class, $baseControllerName);

        $this->setFormTheme(array_merge($this->getFormTheme(),
            array('FestusSiteBundle:Form:image_form.html.twig')
        ));
    }


    protected function configureFormFields(FormMapper $formMapper)
    {
        $formMapper
        ->with('ogólne')
            ->add('title', NULL, array('label' => 'tytuł:'))
                ->add('body', NULL, array('label' => 'treść:', 'attr' => array(
                    'class' => 'tinymce', 'data-theme' => 'simple')))
                ->add('categories', NULL, array('label' => 'kategorie:'))
        ->end()
        ->with('media')
            ->add('fileName', 'text', array(
                    "label" => 'tytuł obrazka:',
                    'property_path' => false,
                        'required' => false
                ))
            ->add('theFile', 'file', array(
                    "label" => 'wybierz plik',
                    'property_path' => false,
                        'required' => false
                ))
            ->end()
        ;
    }

    protected function configureDatagridFilters(DatagridMapper $datagridMapper)
    {
        $datagridMapper
            ->add('title')
            ->add('body')
        ;
    }

    protected function configureListFields(ListMapper $listMapper)
    {
        $listMapper
            ->addIdentifier('title')
            ->add('categories')

            ->add('_action', 'actions', array(
                'actions' => array(
                    'view' => array(),
                    'edit' => array(),
                )
            ))
        ;
    }

    protected function configureShowFields(ShowMapper $showMapper)
    {
        $showMapper->add('title')
            ->add('body');

    }

    public function validate(ErrorElement $errorElement, $object)
    {
        $errorElement
            ->with('title')
                ->assertMinLength(array('limit' => 2))
            ->end()
        ;
    }

    public function prePersist($news) {
        $this->saveFile($news);
      }

      public function preUpdate($news) {
        $this->saveFile($news);
      }

      public function saveFile($news) {
        $request = Request::createFromGlobals();
        $requestData = current($request->request->all());
        $filesData = current($request->files->all());
        $document = new Document();
        $theFile = $filesData['theFile'];
        $name = $requestData['fileName'];

        if($theFile != NULL){
            $document->setName($name);
            $document->setTheFile($theFile);
            $news->setDocument($document);
        }
      }
}

Ma base de la classe bundle s'étend admin de la classe bundle, co j'ai pu remplacer les modèles:

<?php

namespace SomeSiteBundle;

use SymfonyComponentHttpKernelBundleBundle;

class SomeSiteBundle extends Bundle
{

    public function getParent()
    {
        return 'SonataAdminBundle';
    }

}

Et SomeSiteBundle/resources/views/CRUD/base_edit.html.twig j'ai changé un peu de template pour permettre à l'utilisateur de voir l'image en cours de paramétrage:

<div class="sonata-ba-collapsed-fields">                    
                    {% for field_name in form_group.fields %}
                        {% if admin.formfielddescriptions[field_name] is defined %}
                            {% if field_name == 'fileName' %}

                            <h5 style="margin-left: 40px">Obecny obrazek:</h5>
                            {% if object.documents[0] is defined %}
                                <img style="margin: 0 0 0 40px; border: 1px dotted #ccc" src="{{ asset(object.documents[0].webPath) }}" />
                                {% else %}
                                <div style="margin-left: 40px">brak</div>
                                {% endif %}
                                <hr><h5 style="margin-left: 40px">Wczytaj nowy:</h5>
                            {% endif %}
                            {{ form_row(form[field_name])}}
                        {% endif %}
                    {% endfor %}
                </div>

en ce moment, je n'utilise qu'une seule image par news ("featured picture") et c'est un peu exagéré de toute façon, parce que j'utilise tinyMCE avec le plugin jbimages, donc je peux mettre des images dans le corps des news de toute façon. Pour que le plugin jbimages fonctionne correctement, vous devez définir quelques options tinyMCE:

------ cette partie traite de tinymce et tinymce bundle et la tinymce plugin: ---------

$config['img_path'] = '/web/uploads/documents'; (ou tout autre chemin qui vous convient) en web/bundles/stfalcontinymce/vendor/tiny_mce/plugins/jbimages/config.php. (Vous devez installer stfalcon tinymce faisceau d'abord, bien sûr). Puis j'ai édité un peu web/bundles/stfalcontinymce/js/init.jquery.js pour permettre plus d'options de config.yml à lire:

themeOptions.script_url = options.jquery_script_url;
            //mine:
            themeOptions.convert_urls = options.convert_urls;
            themeOptions.relative_urls = options.relative_urls;
            themeOptions.remove_script_host = options.remove_script_host;
            themeOptions.document_base_url = options.document_base_url;

Et enfin config.yml:

[...]
stfalcon_tinymce:
    include_jquery: true
    tinymce_jquery: true
    textarea_class: "tinymce"
    relative_urls : false
    convert_urls : false
    remove_script_host : false
    document_base_url : "http://somesite.home.pl/web/"
    theme:
[...]

et C'est tout, AFAIR. Espérons que cette aide ;-)

16
demandé sur heliogabal 2012-07-17 20:03:23

2 réponses

peut-être que vous pouvez trouver la réponse à votre question dans le: /admin/sonate/media/media/créer?fournisseur=sonate.Média.Fournisseur.image&contexte=par défaut

je suis intéressé à votre autre solution, s'il vous plaît code postal. Merci

3
répondu alainivars 2012-11-27 12:31:01

Considéré comme la meilleure pratique de l'imposition de stockage de fichiers sur un serveur distinct. Qu'en serait-il envoyer un fichier ou un lien vers un fichier, vous pouvez utiliser Symfony byundle https://packagist.org/packages/avtonom/media-storage-client-bundle

if($file instanceof UploadedFile){
    $this->get('avtonom.media_storage_client.manager')->sendFile($file, $clientName, $context);
}
0
répondu Anton 2015-10-28 10:04:10