SonataMediaBundle - comment télécharger des images?

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

J'ai fait quelques admin backend avec sonataAdminBundle et sonataDoctrineORMAdminBundle (et quelques autres), la plupart des choses a fonctionné comme prévu, mais je l'ai laissé de téléchargement de fichiers et de manutention pour plus tard, parce que j'ai pensé: "comment dur qui, éventuellement, peut-être?".

Pour faire une longue histoire courte est-il de la documentation sur la plus simple des choses - c'est à dire avoir les images en pièce jointe à un message ou à l'entrée, comment configurer sonata admin classe, comment l'image de l'écran de pouces en modifier la forme, etc.?

Première page de la documentation se termine par "vous pouvez rendre visite à votre tableau de bord d'administration" comme si je pouvais attendre à des changements importants, là, peut-être gestionnaire de médias et en cours d'exécution, ou de quelque chose. Mais ce n'est pas le cas.

Page suivante traite de heplers brièvement, puis une autre page avec assez compliqué vimeo fournisseur de l'étude de cas.

J'ai cherché partout sur le web et le meilleur que je pouvais venir, a été télécharger le terrain avec l'ajax popup, 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:

/**
 * @ORM\ManyToMany(targetEntity="Application\Sonata\MediaBundle\Entity\Media")
 */
public $images; 

et tous les yaml configs et les gammes sont mises en œuvre.

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

Je suis coincé. J'ai été en mesure de créer des médias "manager" juste en plaine sf2 dans une heure ou deux, mais c'était un autre projet et la réécriture actuel pour ce modèle signifie à partir "from scratch". Ce qui à faire afin de faire sonataMediaBundle avec sonataAdminBundle fonctionnent comme prévu?


EDIT: voici ce que j'ai fait, au lieu de:

Ma classe news (ou tout autre qui a besoin de de téléchargement d'images):

<?php

namespace Some\SiteBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Validator\Constraints as Assert;


/**
 * Some\SiteBundle\Entity\News
 *
 * @ORM\Table(name="news")
 */
class News
{
    /**
     * @var integer $id
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;      


    //some stuff...

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


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

  }

[...]

    /**
     * Add documents
     *
     * @param Festus\SiteBundle\Entity\Document $documents
     */
    public function addDocument(\Festus\SiteBundle\Entity\Document $document)
    {
        $this->documents[] = $document;
    }

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

    /**
     * Get documents
     *
     * @return Doctrine\Common\Collections\Collection 
     */
    public function getDocuments()
    {
        return $this->documents;
    }



    //setters, getters...

Ma classe de document (besoin de changer le nom de la table, parce que j'ai couru dans des problèmes avec les mots réservés sur certains serveurs):

<?php  
namespace Some\SiteBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Some\SiteBundle\Entity\Document
*
* @ORM\Table(name="docs")
* @ORM\Entity
* @ORM\HasLifecycleCallbacks
*/
class Document
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @ORM\Column(type="string", length=255)
* @Assert\NotBlank
*/
private $name;
/**
* @ORM\Column(type="string", length=255, nullable=true)
*/
private $path;
/**
* @Assert\File(maxSize="6000000")
*/
private $theFile;
/**
* @ORM\Column(type="datetime", name="created_at")
* 
* @var DateTime $createdAt
*/
protected $createdAt;
/**
* @ORM\Column(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';
}   
/**
* @ORM\PrePersist()
* @ORM\PreUpdate()
*/
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();
}
}
/**
* @ORM\PostPersist()
* @ORM\PostUpdate()
*/
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);
}
/**
* @ORM\PostRemove()
*/
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 des il est copié à partir de tutoriel symfony2.

Maintenant, pour le contrôleur:

<?php 
namespace Some\SiteBundle;
use Some\SiteBundle\Form\Type\ImageShowType;
use Some\SiteBundle\Entity\Document;
use Sonata\AdminBundle\Admin\Admin;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Validator\ErrorElement;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\AdminBundle\Show\ShowMapper;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\Request;
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 Some\SiteBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class SomeSiteBundle extends Bundle
{
public function getParent()
{
return 'SonataAdminBundle';
}
}

Et dans SomeSiteBundle/resources/views/CRUD/base_edit.html.twig j'ai changé de modèle un peu pour permettre à l'utilisateur de voir actuellement mis en image de:

<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>

Droit maintenant, je suis en utilisant une seule photo par de nouvelles ("featured image") et c'est un peu exagéré quand même, parce que je suis en utilisant tinyMCE avec jbimages plugin, donc je peux mettre des images dans les news du corps de toute façon. Pour faire jbimages plugin de travail de droit que Vous avez à régler certains tinyMCE options:

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

$config['img_path'] = '/web/uploads/documents'; (ou tout autre chemin d'accès qui Vous convient) dans 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 dans 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 😉

source d'informationauteur heliogabal