2012-07-17 11 views
16

Prawdopodobnie powinien być zatytułowany: "SonataMediaBundle - gdzie jest brakujące howto?".SonataMediaBundle - jak przesłać obrazy?

Zrobiłem kilka backendów administracyjnych z sonataAdminBundle i sonataDoctrineORMAdminBundle (i kilkoma innymi), większość rzeczy działała zgodnie z oczekiwaniami, ale zostawiłem przesyłanie plików i obsługę na później, ponieważ pomyślałem "jak bardzo to możliwe? ".

Krótko mówiąc - czy istnieje JAKIEKOLWIEK dokumentacja dotycząca najprostszych rzeczy - tj. Posiadanie obrazów dołączonych do wpisu lub wpisu, jak skonfigurować klasę administratora sonaty, jak wyświetlać kciuki w formie edycji itp.?

Pierwsza strona documentation kończy się słowem "możesz odwiedzić swój panel administracyjny", tak jakbym mógł oczekiwać pewnych istotnych zmian, może menedżer mediów działa i coś w tym stylu. Ale tak nie jest.

Następna strona zajmuje się krótko helikopterami, a następnie kolejna strona z dość skomplikowanym studium przypadku dostawcy Vimeo.

Przeszukałem całą sieć i najlepiej wymyśliłem, zostało wysłane pole z wyskakującym okienkiem ajax i lista przesłanych plików.

W mojej klasie mam admina:

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

w mojej klasie Aktualności:

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

i wszystkie configs YAML oraz tras są realizowane.

Wynik: Fatal error: Call to a member function add() on a non-object in [some-entity].php przy próbie załadowania obrazu i listy wyboru identyfikatorów ze znakiem "plus" (pole sonata_type_model chyba).

utknąłem. Udało mi się stworzyć "menadżera" mediów po prostu w zwykłym sf2 w godzinę lub dwie, ale był to kolejny projekt i przepisanie obecnego do tego wzoru oznacza rozpoczynanie "od zera". A więc - co zrobić, aby sonataMediaBundle wraz z sonataAdminBundle działały zgodnie z oczekiwaniami?


EDIT: oto co zrobiłem zamiast:

Moja klasa News (lub inny, który wymaga przesyłania obrazu):

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

Moja klasa dokument (konieczna zmiana nazwy z poniższych tabeli, ponieważ napotkano problemy z zastrzeżonymi słowami na niektórych serwerach):

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


} 

Jak widać, większość jest skopiowany z samouczka symfony2.

Teraz dla kontrolera:

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

Moja klasa bazowa wiązka biegnie administratora klasy wiązki, co mogę nadpisać szablony:

<?php 

namespace Some\SiteBundle; 

use Symfony\Component\HttpKernel\Bundle\Bundle; 

class SomeSiteBundle extends Bundle 
{ 

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

} 

I SomeSiteBundle/resources/views/CRUD/base_edit.html.twig Zmieniłem szablon trochę aby użytkownik mógł zobaczyć aktualnie ustawione zdjęcie:

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

Właśnie teraz używam ly jedno zdjęcie na wiadomość ("polecane zdjęcie") i jest to trochę przesadzone, ponieważ używam tinyMCE z wtyczką jbimages, więc i tak mogę umieścić zdjęcia w ciele wiadomości. Aby jbimages plugin prawo pracy musisz ustawić kilka opcji TinyMCE choć:

------ ta część oferty z TinyMCE i TinyMCE wiązki i plugin TinyMCE: ---------

$config['img_path'] = '/web/uploads/documents'; (lub dowolna inna ścieżka, która Ci odpowiada) w web/bundles/stfalcontinymce/vendor/tiny_mce/plugins/jbimages/config.php. (Najpierw musisz zainstalować pakiet tinfali stfalcon). Potem edytowany trochę web/bundles/stfalcontinymce/js/init.jquery.js aby umożliwić więcej opcji od config.yml należy czytać:

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; 

I wreszcie w 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: 
[...] 

I to wszystko, AFAIR. Nadzieję, że to pomaga ;-)

+3

Ok, zdecydowałem się porzucić mediabundle całkowicie teraz i wdrożone zwykły SF2/doctrine schemat przesyłania plików. Mogę opublikować odpowiedni kod źródłowy dla moich zajęć, jeśli ktoś jest zainteresowany. – heliogabal

+0

Jestem zainteresowany, proszę napisać kod. Dzięki. – Zeljko

Odpowiedz

3

Może znajdziesz odpowiedzi na swoje pytania w: /admin/sonata/media/media/create?provider=sonata.media.provider.image & kontekście = default

Jestem zainteresowany twoim innym rozwiązaniem, proszę opublikuj kod. Dzięki

0

Uznano za najlepszą praktykę wprowadzania przechowywania plików na oddzielnym serwerze. Co byłoby wysłać plik lub link do pliku, można użyć 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); 
}