Ответ 1
Возможно, вы можете найти ответ на свой вопрос в: /admin/sonata/media/media/create?provider=sonata.media.provider.image&context=default
Мне интересно ваше другое решение, пожалуйста, отправьте код. Благодаря
Вероятно, должен быть назван: "SonataMediaBundle - где отсутствует Howto?".
Я сделал несколько бэкэндов для администратора с помощью sonataAdminBundle и sonataDoctrineORMAdminBundle (и некоторых других), большинство из них работало, как ожидалось, но я оставил загрузку и обработку файлов позже, потому что я подумал: "Как сильно это возможно?",
Короче говоря, есть ли какая-либо документация о простейших вещах, т.е. наличие изображений, прикрепленных к сообщению или записи, как настроить класс администратора sonata, как отображать большие пальцы в форме редактирования и т.д.? >
Первая страница документация заканчивается на "вы можете посетить панель управления администратора", как если бы я мог ожидать некоторые соответствующие изменения там, может быть, менеджер мультимедиа запущен или что-то в этом роде. Но это не так.
В следующей статье кратко рассказывается о heplers, а затем о другой странице с довольно сложным примером примера vimeo.
Я искал по всему Интернету и лучше всего мог придумать, было поле для загрузки с всплывающим окном ajax и списком загруженных файлов.
В моем классе admin у меня есть:
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->with('general')
->add('title')
->add('body')
->add('categories')
->end()
->with('media')
->add('images', 'sonata_type_model')
в моем классе новостей:
/**
* @ORM\ManyToMany(targetEntity="Application\Sonata\MediaBundle\Entity\Media")
*/
public $images;
и все конфигурации и маршруты yaml реализованы.
Результат: Fatal error: Call to a member function add() on a non-object in [some-entity].php
при попытке загрузить изображение и выбираемый список идентификаторов изображений со знаком "плюс" (предположим, что имя sonata_type_model).
Я застрял. Я смог создать медиа-менеджер "просто" в обычном sf2 через час или два, но это был еще один проект и переписывание текущего в этот шаблон означает запуск "с нуля". Итак - что делать, чтобы сделать sonataMediaBundle вместе с sonataAdminBundle работать как ожидалось?
EDIT: вот что я сделал вместо этого:
Мой класс новостей (или любой другой, который требует загрузки изображения):
<?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);
}
}
}
Мой базовый класс пакетов расширяет класс bundle admin, я могу перезаписать шаблоны:
<?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>
Сейчас я использую только одно изображение на новости ( "featured picture" ), и это все равно слишком много, потому что я использую tinyMCE с плагином jbimages, поэтому я могу поместить изображения в тело новостей в любом случае. Чтобы плагин jbimages работал правильно, вы должны установить некоторые параметры tinyMCE, хотя:
------ эта часть посвящена подключению tinymce и tinymce bundle и tinymce: ---------
$config['img_path'] = '/web/uploads/documents';
(или любой другой путь, который вам подходит) в web/bundles/stfalcontinymce/vendor/tiny_mce/plugins/jbimages/config.php
. (Сначала вам нужно установить пакет stfalcon tinymce, конечно). Затем я отредактировал бит 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:
[...]
И это все, AFAIR. Надеюсь, что это поможет; -)
Возможно, вы можете найти ответ на свой вопрос в: /admin/sonata/media/media/create?provider=sonata.media.provider.image&context=default
Мне интересно ваше другое решение, пожалуйста, отправьте код. Благодаря
Учитывается наилучшая практика наложения файлового хранилища на отдельный сервер. Что бы отправить файл или ссылку на файл, вы можете использовать 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);
}