SonataMediaBundle - 如何上传图片?

16

可能应该被命名为:“SonataMediaBundle-缺失的howto在哪里?”。

我使用了sonataAdminBundle和sonataDoctrineORMAdminBundle(以及其他一些)来创建一些管理后台,大部分事情都按预期工作,但我把文件上传和处理留到了后面,因为我认为“这有多难呢?”。

长话短说-是否有任何关于最简单的事情的文档-i.e.如何将图片附加到帖子或条目,如何配置sonata admin类,如何在编辑表单中显示图像缩略图等?

文档的第一页以“您可以访问您的管理仪表板”结束,好像我可以在那里期望一些相关的更改,也许是媒体管理器正在运行,或者其他什么。但实际上并不是这样。

下一页简要介绍了助手,然后是另一页,涉及相当复杂的vimeo提供商案例研究。

我在网上搜索了所有内容,最好的结果是使用ajax弹出窗口的上传字段和已上传文件列表。

在我的管理类中,我有:

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

在我的News类中:
/**
 * @ORM\ManyToMany(targetEntity="Application\Sonata\MediaBundle\Entity\Media")
 */
public $images; 

所有的yaml配置和路由都已经实现。

结果是:在尝试上传图片时,选择图片id列表并带有“加号”标志(我猜这是sonata_type_model字段)会显示错误消息:Fatal error: Call to a member function add() on a non-object in [some-entity].php

我被卡住了。我用sf2只花了一两个小时就创建了一个媒体“管理器”,但那是另一个项目,将当前项目重写为这种模式意味着从头开始。所以 - 为了使sonataMediaBundle与sonataAdminBundle正常工作,该怎么办?


编辑:这是我所做的:

我的新闻类(或其他需要上传图片的类):

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

我的文档类(需要更改表名,因为在某些服务器上遇到了保留字问题):

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


}

正如您所见,大部分内容都是从Symfony2教程中复制的。

现在,让我们来看控制器:

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

我的基础包类继承自管理包类,所以我可以覆盖模板:
<?php

namespace Some\SiteBundle;

use Symfony\Component\HttpKernel\Bundle\Bundle;

class SomeSiteBundle extends Bundle
{

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

}

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>

目前我在每篇新闻中只使用一张图片(“特色图片”),但这也有点过度,因为我正在使用带有jbimages插件的tinyMCE,所以我可以将图片放入新闻正文中。要使jbimages插件正常工作,您必须设置一些tinyMCE选项:

------ 这部分涉及tinymce、tinymce捆绑包和tinymce插件: ---------

$config['img_path'] = '/web/uploads/documents'; (或适合您的任何其他路径)在web/bundles/stfalcontinymce/vendor/tiny_mce/plugins/jbimages/config.php中。(当然,您需要首先安装stfalcon tinymce bundle)。然后,我编辑了一下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;

最后,在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:
[...]

这就是所有的内容了,尽我所知是这样的。希望这会有所帮助;-)

3
好的,我已经决定暂时放弃使用mediabundle并实现普通的sf2/doctrine文件上传方案。如果有人感兴趣,我可以发布相关类的源代码。 - heliogabal
我很感兴趣,请发布一段代码。谢谢。 - Zeljko
2个回答

3
也许您可以在此处找到问题的答案: /admin/sonata/media/media/create?provider=sonata.media.provider.image&context=default
我对您的其他解决方案很感兴趣,请发布代码。谢谢

0

将文件存储在单独的服务器上被认为是最佳实践。如果要发送文件或文件链接,您可以使用Symfony Bundle https://packagist.org/packages/avtonom/media-storage-client-bundle

if($file instanceof UploadedFile){
    $this->get('avtonom.media_storage_client.manager')->sendFile($file, $clientName, $context);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接