Skip to content

Instantly share code, notes, and snippets.

@pleone
Last active March 30, 2021 11:36
Show Gist options
  • Save pleone/f15682baa86781ba90fb to your computer and use it in GitHub Desktop.
Save pleone/f15682baa86781ba90fb to your computer and use it in GitHub Desktop.
Sonata admin embedded form with image preview
<?php
namespace Acme\CoreBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\Validator\Constraints as Assert;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
use Gedmo\Mapping\Annotation as Gedmo;
use Doctrine\Common\Collections\ArrayCollection;
use JMS\Serializer\Annotation as Serializer;
/**
* News
*
* @ORM\Table( name="news", indexes={ @ORM\Index(name="idxyear", columns={"year"}), @ORM\Index(name="idxcategory", columns={"category_id"}) } )
* @ORM\Entity(repositoryClass="Acme\CoreBundle\Repository\NewsRepository")
* @Gedmo\TranslationEntity(class="Acme\CoreBundle\Entity\NewsTranslation")
* @Vich\Uploadable
* @Serializer\ExclusionPolicy("all")
*/
class News
{
/**
* @var integer
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
* @Serializer\Expose
* @Serializer\Groups({"list","details"})
*/
private $id;
/**
* @Assert\File(
* maxSize="2M",
* mimeTypes={"image/png", "image/jpeg", "image/pjpeg"}
* )
* @Vich\UploadableField(mapping="news_image", fileNameProperty="visual")
*
* @var File $image
*/
private $image;
/**
* @Assert\File(
* maxSize="2M",
* mimeTypes={"image/png", "image/jpeg", "image/pjpeg"}
* )
* @Vich\UploadableField(mapping="news_image", fileNameProperty="listingImage")
*
* @var File $image
*/
private $smallimage;
/**
* @var string
*
* @ORM\Column(name="visual", type="string", length=255)
*/
private $visual;
/**
* @var string
*
* @ORM\Column(name="listingImage", type="string", length=255, nullable=true)
*/
private $listingImage;
/**
* @var string
* @Gedmo\Translatable
* @ORM\Column(name="title", type="string", length=255,nullable=true)
* @Serializer\Expose
* @Serializer\Groups({"list","details"})
*/
private $title;
/**
* @var string
* @Gedmo\Translatable
* @ORM\Column(name="subtitle", type="string", length=255, nullable=true)
* @Serializer\Expose
* @Serializer\Groups({"list","details"})
*/
private $subtitle;
/**
* @var string
* @Gedmo\Translatable
* @ORM\Column(name="body", type="text",nullable=true)
* @Serializer\Expose
* @Serializer\Groups({"details"})
*/
private $body;
/**
* @var boolean
*
* @ORM\Column(name="homepage", type="boolean", nullable=true)
*/
private $homepage;
/**
* @var \DateTime
*
* @ORM\Column(name="publishDate", type="datetime", nullable=true)
* @Serializer\Expose
* @Serializer\Groups({"list","details"})
*/
private $publishDate;
/**
* @var datetime $created
*
* @Gedmo\Timestampable(on="create")
* @ORM\Column(type="datetime")
*/
private $created;
/**
* @var datetime $updated
*
* @Gedmo\Timestampable(on="update")
* @ORM\Column(type="datetime")
*/
private $updated;
/**
* @ORM\OneToMany(
* targetEntity="NewsTranslation",
* mappedBy="object",
* cascade={"persist", "remove"}
* )
*/
private $translations;
/**
* @Gedmo\Translatable
* @Gedmo\Slug(fields={"title"})
* @ORM\Column(length=128)
* @Serializer\Expose
* @Serializer\Groups({"list","details"})
*
*/
private $slug;
/**
* @var integer
*
* @ORM\Column(name="category_id", type="integer", nullable=true)
* @Serializer\Expose
* @Serializer\Groups({"list","details"})
*/
private $category;
/**
* @ORM\OneToMany(
* targetEntity="NewsSlider",
* mappedBy="news",
* orphanRemoval=true,
* cascade={"persist", "remove"}
* )
* @ORM\OrderBy({"rank" = "ASC"})
*
* orphanRemoval=true is necessary to allow the delete option in sonata
* The order by is a nice feature to have the slider image ordered by rank in sonata too
*/
private $slider;
/**
* Constructor
*/
public function __construct()
{
$this->translations = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* ... getters / setters
*/
}
<?php
namespace Acme\AdminBundle\Admin;
use Gedmo\Sluggable\Util\Urlizer;
use Sonata\AdminBundle\Admin\Admin;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Form\FormMapper;
use Acme\CoreBundle\Entity\NewsTranslation;
class NewsAdmin extends Admin
{
protected $categories;
// Fields to be shown on create/edit forms
protected function configureFormFields(FormMapper $formMapper)
{
$visual = $images->getVisual();
// use $fileFieldOptions so we can add other options to the field
$fileFieldOptionsVisual = array('required' => false, 'help' => '<p>Dimensioni immagine: 1600px x 900px. </p>');
if ($visual && ($webPath = $images->getWebPath())) {
// get the container so the full path to the image can be set
$container = $this->getConfigurationPool()->getContainer();
$fullPath = $container->get('request')->getBasePath().'/'.$webPath.$visual;
// add a 'help' option containing the preview's img tag
$fileFieldOptionsVisual['help'] = '<p>Dimensioni: 1600px x 900px</p> <img src="'.$fullPath.'" class="admin-preview" style="max-height: 200px; max-width: 200px;" />';
}
$formMapper
->add('category', 'choice', array(
'label' => 'Categoria',
'required' => false,
'choices' => $this->categories,
))
->add('publishDate', 'datetime', array('label' => 'Data pubblicazione', 'required' => true))
->add('title', null, array('label' => 'Titolo', 'required' => true))
->add('subtitle', null, array('label' => 'Sottotitolo', 'required' => false))
->add('slug', null, array('required' => false, 'help' => 'Se vuoto è autogenerato'))
->add('body', null, array(
'label' => 'Body',
'required' => false,
'attr' => array(
'class' => 'tinymce',
'style' => 'width:70%; height:250px',
)
))
->add('homepage', 'checkbox', array("label" => "Show in homepage", "required" => false))
->end()
->with('Immagini')
->add('image', 'file', $fileFieldOptionsVisual)
->end()
->with('Traduzioni')
->add('translations', 'a2lix_translations_gedmo', array(
'required' => true,
'translatable_class' => "Acme\CoreBundle\Entity\News"))
//http://a2lix.fr/bundles/translation-form/1.4.html
->add('translations', 'a2lix_translations_gedmo', array(
'required' => true, // [2]
'fields' => array( // [3]
'body' => array( // [3.a]
//'field_type' => 'ckeditor', // [4]
//'label' => 'Body',
//'config_name' => 'my_config',
)
)
))
->end()
->with('Slider')
->add('slider', 'sonata_type_collection', array(
'by_reference' => true,
'type_options' => array('delete' => true),
), array(
'edit' => 'inline',
//'inline' => 'table',
//'sortable' => 'rank',
))
->end()
;
}
// Fields to be shown on filter forms
protected function configureDatagridFilters(DatagridMapper $datagridMapper)
{
$datagridMapper
->add('title')
->add('subtitle')
;
}
// Fields to be shown on lists
protected function configureListFields(ListMapper $listMapper)
{
$listMapper
->addIdentifier('title')
->add('subtitle')
->add('slug')
//->add('image', 'image', array('label' => 'Immagine'))
->add('_action', 'actions', array(
'actions' => array(
'delete' => array('template' => 'AcmeAdminBundle:Admin:_delete.html.twig'),
'edit' => array('template' => 'AcmeAdminBundle:Admin:_edit.html.twig'),
)))
;
}
public function prePersist($obj) {
$this->manageFileUpload($obj);
$obj->setSlug($this->urlify($obj->getTitle()));
$this->manageTranslationSlug($obj);
foreach ($obj->getSlider() as $slide) {
$slide->setNews($obj);
}
$this->manageEmbeddedImageAdmins($obj);
}
public function preUpdate($obj) {
$translations= $obj->getTranslations();
foreach($translations as $word){
if($word->getField() == "title" && $word->getLocale() == "it"){
$title = $word->getContent();
}
}
$this->manageFileUpload($obj);
$this->manageTranslationSlug($obj);
//$obj->setSlug($this->urlify($title));
foreach ($obj->getSlider() as $slide) {
$slide->setNews($obj);
}
$this->manageEmbeddedImageAdmins($obj);
}
private function manageTranslationSlug($obj) {
$titles = array();
$slugs = array();
$translations= $obj->getTranslations();
foreach($translations as $word){
if($word->getField() == "title"){
if (!$word->getContent()) {
$word->setContent( $obj->getTitle() );
}
$titles[ $word->getLocale() ] = $word->getContent();
}
if($word->getField() == "slug"){
$slugs[ $word->getLocale() ] = $word->getContent();
if (!$word->getContent()) {
$word->setContent( Urlizer::urlize($titles[$word->getLocale()]) );
}
}
}
}
private function manageFileUpload($image) {
if ($image->getImage()) {
$image->setUpdated(new \DateTime('now'));
}
}
private function urlify($string){
return Urlizer::urlize($string);
}
public function setCategories($categories)
{
$this->categories = $categories;
}
// https://github.com/sonata-project/SonataAdminBundle/blob/master/Resources/doc/cookbook/recipe_file_uploads.rst
// http://stackoverflow.com/questions/19439696/sonata-admin-collection-with-sort-and-delete
private function manageEmbeddedImageAdmins($news) {
// Cycle through each field
foreach ($this->getFormFieldDescriptions() as $fieldName => $fieldDescription) {
// detect embedded Admins that manage Images
if ($fieldDescription->getType() === 'sonata_type_collection' &&
($associationMapping = $fieldDescription->getAssociationMapping()) &&
$associationMapping['targetEntity'] === 'Acme\CoreBundle\Entity\NewsSlider'
) {
$getter = 'get' . $fieldName;
$setter = 'set' . ucfirst($fieldName);
/** @var Image $image */
$images = $news->$getter();
foreach($images as $image ){
if ($image) {
if ($image->getImage()) {
// update the Image to trigger file management
$image->refreshUpdated();
} elseif (!$image->getImage() ) {
// prevent Sf/Sonata trying to create and persist an empty Image
//$news->removeSlider($image);
$news->$setter(null);
}
}
}
}
}
}
}
<?php
namespace Acme\CoreBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\Validator\Constraints as Assert;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
use Gedmo\Mapping\Annotation as Gedmo;
use Doctrine\Common\Collections\ArrayCollection;
use JMS\Serializer\Annotation as Serializer;
/**
* NewsSlider
*
* @ORM\Table()
* @Vich\Uploadable
* @ORM\Entity(repositoryClass="Acme\CoreBundle\Entity\NewsSliderRepository")
*/
class NewsSlider extends EntityWithImage
{
protected $upload_dir = 'news';
/**
* @var integer
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @Assert\File(
* maxSize="2M",
* mimeTypes={"image/png", "image/jpeg", "image/pjpeg"}
* )
* @Vich\UploadableField(mapping="news_slider_image", fileNameProperty="imageName")
*
* @var File $image
*/
private $image;
/**
* @ORM\Column(type="string", length=255, name="image_name", nullable=true)
*
* @var string $imageName
*/
protected $imageName;
/**
* @var integer
*
* @ORM\Column(name="rank", type="integer", nullable=true)
*/
private $rank;
/**
* @var string
*
* @ORM\Column(name="title", type="string", length=255, nullable=true)
*/
private $title;
/**
* @var string
*
* @ORM\Column(name="description", type="string", length=255, nullable=true)
*/
private $description;
/**
* @ORM\ManyToOne(targetEntity="News", inversedBy="slider")
* @ORM\JoinColumn(name="news_id", referencedColumnName="id", onDelete="CASCADE", nullable=true)
* @Serializer\Expose
* @Serializer\Groups({"list","details"})
*/
protected $news;
/**
* @var datetime $created
*
* @Gedmo\Timestampable(on="create")
* @ORM\Column(type="datetime")
*/
private $created;
/**
* @var datetime $updated
*
* @Gedmo\Timestampable(on="update")
* @ORM\Column(type="datetime")
*/
private $updated;
/**
* getters/setters
*/
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment