简体   繁体   English

SonataMediaBundle - 如何上传图片?

[英]SonataMediaBundle - how to upload images?

Probably should be titled: "SonataMediaBundle - where's the missing howto?". 可能应该标题为:“SonataMediaBundle - 失踪的howto在哪里?”。

I've made some admin backend with sonataAdminBundle and sonataDoctrineORMAdminBundle (and some other), most of the things worked as expected, but I left file upload and handling for later, because I thought "how hard that possibly can be?". 我已经使用sonataAdminBundle和sonataDoctrineORMAdminBundle(以及其他一些)制作了一些管理员后端,大部分工作都按预期工作,但我留下文件上传和处理以供日后使用,因为我认为“可能有多难?”。

To make long story short - is there ANY documentation about simplest of things - ie having images attached to a post or entry, how to configure sonata admin class, how to display image thumbs in edit form, etc.? 长话短说 - 是否有关于最简单的事情的任何文档 - 即将图像附加到帖子或条目,如何配置奏鸣曲管理类,如何以编辑形式显示图像拇指等?

First page of documentation ends with "you can visit your admin dashboard" as if I could expect some relevant changes there, maybe media manager up and running, or something. 文档的第一页以“您可以访问您的管理仪表板”结束,就像我可以期待那里的一些相关更改,可能是媒体经理启动和运行,或者其他什么。 But this is not the case. 但这种情况并非如此。

Next page deals with heplers briefly, and then another page with fairly complicated vimeo provider case study. 下一页简要介绍了heplers,然后是另一个页面,其中包含相当复杂的vimeo提供程序案例研究。

I've searched all over the web and best I could come up with, was upload field with ajax popup, and list of uploaded files. 我在网上搜索过,我最好能想出来,上传带有ajax弹出窗口的字段,以及上传文件列表。

In my admin class I've got: 在我的管理课程中,我有:

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

in my News class: 在我的新闻课上:

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

and all the yaml configs and routings are implemented. 并实施了所有的yaml配置和路由。

The result is: Fatal error: Call to a member function add() on a non-object in [some-entity].php when trying to upload an image, and selectable list of image id's with "plus" sign (sonata_type_model field I guess). 结果是: Fatal error: Call to a member function add() on a non-object in [some-entity].php在尝试上传图像时Fatal error: Call to a member function add() on a non-object in [some-entity].php ,以及带有“加号”符号的图像id的可选列表(sonata_type_model field I猜测)。

I'm stuck. 我被卡住了。 I was able to create media "manager" just in plain sf2 in an hour or two, but it was another project and rewriting current one to this pattern means starting "from scratch". 我能够在一两个小时内以简单的sf2创建媒体“经理”,但这是另一个项目,并且重写当前的模式意味着从头开始“开始”。 So - what to do in order to make sonataMediaBundle together with sonataAdminBundle work as expected? 那么 - 为了使sonataMediaBundle与sonataAdminBundle一起按预期工作怎么办?


EDIT: here's what I did instead: 编辑:这是我做的事情:

My news class (or any other which needs image upload): 我的新闻类(或任何其他需要图片上传的内容):

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

My document class (needed to change the name of the table, because I ran into issues with reserved words on some servers): 我的文档类(需要更改表的名称,因为我在某些服务器上遇到了保留字的问题):

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


}

As You can see, most of it is copied from symfony2 tutorial. 正如您所看到的,大部分内容都是从symfony2教程中复制而来的。

Now, for the controller: 现在,对于控制器:

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

My base bundle class extends admin bundle class, co I could overwrite templates: 我的基础包类扩展了管理包类,我可以覆盖模板:

<?php

namespace Some\SiteBundle;

use Symfony\Component\HttpKernel\Bundle\Bundle;

class SomeSiteBundle extends Bundle
{

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

}

And in SomeSiteBundle/resources/views/CRUD/base_edit.html.twig I've changed template a little bit to let user see currently set picture: SomeSiteBundle/resources/views/CRUD/base_edit.html.twig我稍微更改了模板,让用户看到当前设置的图片:

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

Right now I'm using only one picture per news ("featured picture") and it's a bit overkill anyway, because I'm using tinyMCE with jbimages plugin, so I can put images into news body anyway. 现在我每个新闻只使用一张图片(“精选图片”),无论如何都有点矫枉过正,因为我正在使用带有jbimages插件的tinyMCE,所以无论如何我都可以把图像放到新闻体中。 To make jbimages plugin work right You've got to set some tinyMCE options though: 使jbimages插件正常工作你必须设置一些tinyMCE选项:

------ this part deals with tinymce and tinymce bundle and tinymce plugin: --------- ------这部分涉及tinymce和tinymce bundle和tinymce插件:---------

$config['img_path'] = '/web/uploads/documents'; (or any other path that suits You) in web/bundles/stfalcontinymce/vendor/tiny_mce/plugins/jbimages/config.php . (或任何其他适合你的路径)在web/bundles/stfalcontinymce/vendor/tiny_mce/plugins/jbimages/config.php (You need to install stfalcon tinymce bundle first, of course). (当然,首先需要安装stfalcon tinymce bundle)。 Then I edited a bit web/bundles/stfalcontinymce/js/init.jquery.js to allow more options from config.yml to be read: 然后我编辑了一下web/bundles/stfalcontinymce/js/init.jquery.js以允许读取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;

And finally in config.yml : 最后在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:
[...]

And that's all, AFAIR. 这就是全部,AFAIR。 Hope this helps ;-) 希望这可以帮助 ;-)

Maybe you can find the answer to your question in the: /admin/sonata/media/media/create?provider=sonata.media.provider.image&context=default 也许你可以在:/admin/sonata/media/media/create?provider=sonata.media.provider.image&context=default中找到问题的答案。

I am interested to your other solution, please post code. 我对您的其他解决方案感兴趣,请发布代码。 Thanks 谢谢

Considered the best practice of the imposition of file storage on a separate server. 考虑在单独的服务器上强加文件存储的最佳实践。 What would it send a file or a link to a file, you can use Symfony byundle https://packagist.org/packages/avtonom/media-storage-client-bundle 什么会发送文件或链接到文件,你可以使用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);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM