yml services register name container symfony symfony-sonata sonata-admin

symfony - services - SonataMediaBundle-¿Cómo subir imágenes?



symfony bind (2)

Probablemente debería tener el título: "SonataMediaBundle - ¿dónde está el howto que falta?".

He creado algún backend de administración con sonataAdminBundle y sonataDoctrineORMAdminBundle (y algunos otros), la mayoría de las cosas funcionaron como se esperaba, pero dejé la carga y el manejo del archivo para más tarde, porque pensé "¿qué tan difícil puede ser?".

Para resumir, ¿existe ALGUNA documentación sobre las cosas más simples, es decir, tener imágenes adjuntas a una publicación o entrada, cómo configurar la clase de administración de sonata, cómo mostrar los pulgares de las imágenes en forma de edición, etc.?

La primera página de la documentation termina con "puede visitar su panel de administración" como si pudiera esperar algunos cambios relevantes allí, tal vez el administrador de medios en funcionamiento, o algo así. Pero este no es el caso.

La siguiente página trata con los analizadores brevemente, y luego otra página con un caso de estudio de proveedor de Vimeo bastante complicado.

He buscado en toda la web y lo mejor que pude encontrar fue el campo de carga con ajax emergente y la lista de archivos cargados.

En mi clase de administración tengo:

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

en mi clase de Noticias:

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

y todas las configuraciones y enrutamientos yaml están implementados.

El resultado es: Fatal error: Call to a member function add() on a non-object in [some-entity].php cuando se intenta cargar una imagen, y una lista seleccionable de identificadores de imagen con el signo "más" (campo sonata_type_model I adivinar).

Estoy atascado. Pude crear un "administrador" de medios solo en el plano sf2 en una o dos horas, pero era otro proyecto y reescribir el actual para este patrón significa comenzar "desde cero". Entonces, ¿qué hacer para que sonataMediaBundle y sonataAdminBundle funcionen como se espera?

EDIT: esto es lo que hice en su lugar:

Mi clase de noticias (o cualquier otra que necesite subir una imagen):

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

Mi clase de documento (necesario para cambiar el nombre de la tabla, porque tuve problemas con las palabras reservadas en algunos servidores):

<?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; } }

Como puedes ver, la mayoría se copia desde el tutorial de symfony2.

Ahora, para el controlador:

<?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); } } }

Mi clase de paquete base extiende la clase de paquete de administración, podría sobrescribir las plantillas:

<?php namespace Some/SiteBundle; use Symfony/Component/HttpKernel/Bundle/Bundle; class SomeSiteBundle extends Bundle { public function getParent() { return ''SonataAdminBundle''; } }

Y en SomeSiteBundle/resources/views/CRUD/base_edit.html.twig , he cambiado la plantilla un poco para que el usuario vea la imagen actualmente configurada:

<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 este momento estoy usando solo una imagen por noticia ("foto destacada") y es un poco excesivo de todos modos, porque estoy usando tinyMCE con el complemento jbimages, así que puedo poner las imágenes en el cuerpo de noticias de todos modos. Para hacer que el complemento de jbimages funcione correctamente, sin embargo, tiene que configurar algunas opciones de tinyMCE:

------ esta parte trata sobre tinymce y tinymce bundle y tinymce plugin: ---------

$config[''img_path''] = ''/web/uploads/documents''; (o cualquier otra ruta que se adapte a Usted) en web/bundles/stfalcontinymce/vendor/tiny_mce/plugins/jbimages/config.php . (Es necesario instalar primero el paquete stfalcon tinymce, por supuesto). Luego web/bundles/stfalcontinymce/js/init.jquery.js un poco web/bundles/stfalcontinymce/js/init.jquery.js para permitir leer más opciones de config.yml :

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;

Y finalmente en 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: [...]

Y eso es todo, AFAIR. Espero que esto ayude ;-)


Considerada la mejor práctica de la imposición de almacenamiento de archivos en un servidor separado. ¿Qué enviaría un archivo o un enlace a un archivo? Puede usar 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); }


Quizás pueda encontrar la respuesta a su pregunta en: /admin/sonata/media/media/create?provider=sonata.media.provider.image&context=default

Estoy interesado en su otra solución, por favor coloque el código. Gracias