Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
300 views
in Technique[技术] by (71.8m points)

php - Handling multiple file uploads in Sonata Admin Bundle

So, after research a lot and get no results (maybe I'm a bad searcher) I coming from this topics: SonataAdmin Bundle File Upload Error and SonataMediaBundle - how to upload images? I can't find a solution for my problem. I have a Entity Company and each company can have multiple files: PDF, DOC, XLS and some other mime/types. I think to use VichUploaderBundle but again docs only covers example for one to one relationship so my question is, any can give me some examples or ways to get this done? I mean upload files and attach them to company?

EDIT1 working and testing

As I said before I'm trying to integrate SonataMediaBundle into another admin module I have but I can't get it to work. What I did until now?

Of course install and configure all bundles: SonataAdminBundle and SonataMediaBundle both are working fine

Modified ApplicationSonataMediaBundleEntityMedia.php class to add the needed functionality by adding a ManyToMany relationship

namespace ApplicationSonataMediaBundleEntity;

use SonataMediaBundleEntityBaseMedia as BaseMedia;
use DoctrineORMMapping as ORM;

class Media extends BaseMedia {

    /**
     * @var integer $id
     */
    protected $id;

    /**
     * @ORMManyToMany(targetEntity="PLOrderBundleEntityOrder", inversedBy="medias")
     * @ORMJoinTable(name="order_has_media__media",
     *      joinColumns={@ORMJoinColumn(name="media__media_id", referencedColumnName="id")},
     *      inverseJoinColumns={@ORMJoinColumn(name="order_no_order", referencedColumnName="no_order")}
     * )
     */
    protected $orders;

    public function __construct() {
        $this->orders = new DoctrineCommonCollectionsArrayCollection();
    }

    /**
     * Get id
     *
     * @return integer $id
     */
    public function getId() {
        return $this->id;
    }

    public function setOrders(PLOrderBundleEntityOrder $order) {
        $this->orders[] = $order;
    }

    public function getOrders() {
        return $this->orders;
    }

}

Adding the need fields in PLOrderBundleEntityOrder.php as follow:

namespace PLOrderBundleEntity;

use DoctrineORMMapping as ORM;

/**
 * @ORMEntity
 * @ORMTable(name="tb_order")
 */
class Order {

    /**
     * @ORMId
     * @ORMColumn(type="string", length=15, unique=true, nullable=false)
     */
    protected $no_order;

    /**
     * @ORMManyToOne(targetEntity="PLCompanyBundleEntityCompany", inversedBy="id")
     */
    protected $company;

    /**
     * @ORMColumn(type="string", length=15, unique=true)
     */
    protected $business_case;

    /**
     * @ORMColumn(type="integer", length=1)
     */
    protected $charge_status;

    /**
     * @ORMColumn(type="datetime")
     */
    protected $eta;

    /**
     * @ORMColumn(type="datetime")
     */
    protected $etd;

    /**
     * @ORMColumn(type="integer", length=1)
     */
    protected $transport_media;

    /**
     * @ORMColumn(type="integer", length=1)
     */
    protected $incoterm;

    /**
     * @ORMColumn(type="string", length=250)
     */
    protected $comments;

    /**
     * @ORMManyToMany(targetEntity="ApplicationSonataMediaBundleEntityMedia", mappedBy="orders")
     */
    protected $medias;

    public function __construct() {
        $this->medias = new DoctrineCommonCollectionsArrayCollection();
    }

    public function setNoOrder($no_order) {
        $this->no_order = $no_order;
    }

    public function getNoOrder() {
        return $this->no_order;
    }

    public function setCompany(PLCompanyBundleEntityCompany $company) {
        $this->company = $company;
    }

    public function getCompany() {
        return $this->company;
    }

    public function setBusinessCase($business_case) {
        $this->business_case = $business_case;
    }

    public function getBusinessCase() {
        return $this->business_case;
    }

    public function setChargeStatus($charge_status) {
        $this->charge_status = $charge_status;
    }

    public function getChargeStatus() {
        return $this->charge_status;
    }

    public function setETA($eta) {
        $this->eta = $eta;
    }

    public function getETA() {
        return $this->eta;
    }

    public function setETD($etd) {
        $this->etd = $etd;
    }

    public function getETD() {
        return $this->etd;
    }

    public function setTransportMedia($transport_media) {
        $this->transport_media = $transport_media;
    }

    public function getTransportMedia() {
        return $this->transport_media;
    }

    public function setIncoterm($incoterm) {
        $this->incoterm = $incoterm;
    }

    public function getIncoterm() {
        return $this->incoterm;
    }

    public function setComments($comments) {
        $this->comments = $comments;
    }

    public function getComments() {
        return $this->comments;
    }

    public function setMedias(ApplicationSonataMediaBundleEntityMedia $media) {
        $this->medias[] = $media;
    }

    public function addMedia(ApplicationSonataMediaBundleEntityMedia $media) {
        $this->medias[] = $media;
    }

    public function getMedias() {
        return $this->medias;
    }

}

Changed the configureFormFields in OrderAdmin.php file as follow:

protected function configureFormFields(FormMapper $form) {
        $form
                ->add('no_order', null, array('label' => 'No. Order'))
                ->add('company', 'entity', array('class' => 'PLCompanyBundleEntityCompany', 'label' => 'Cliente'))
                ->add('business_case', null, array('label' => 'BC'))
                ->add('charge_status', 'choice', array('choices' => array(
                        "empty_value" => "Seleccione una opción",
                        "0" => "Ninguno",
                        "1" => "Proceso de Fabricacion",
                        "2" => "Pickup en destino",
                        "3" => "A la espera de recojo por cliente",
                        "4" => "Carga en transito",
                        "5" => "Carga arribada",
                        "6" => "En proceso de aduana",
                        "7" => "Entregado a cliente",
                        "8" => "En bodega"
                    ), "required" => true, 'label' => 'Estado de la carga'))
                ->add('eta', null, array('label' => 'ETD'))
                ->add('etd', null, array('label' => 'ETA'))
                ->add('transport_media', 'choice', array('choices' => array("empty_value" => "Seleccione una opción", "0" => "EXW", "1" => "Maritimo", "2" => "Aereo"), "required" => true, 'label' => 'Via de Transporte'))
                ->add('incoterm', 'choice', array('choices' => array(
                        "empty_value" => "Seleccione una opción",
                        "0" => "Ninguno",
                        "1" => "EWX",
                        "2" => "FOB",
                        "3" => "CIF",
                        "4" => "DDP"
                    ), "required" => true, 'label' => 'Incoterm'))
                ->add('comments', null, array('label' => 'Comentarios'))
                ->add('medias', 'sonata_type_collection', array(
                    'label' => 'Documentos',
                    'type_options' => array('delete' => true)), array(
                    'edit' => 'inline', 'inline' => 'table', 'sortable' => 'position')
        );
    }

But this doesn't work since I can't upload any file and this is what I want upload many files from the same form and attach them to the order I'm creating. See the attached images for a visual I get when I access the create action:

enter image description here enter image description here

What I'm missing?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

For your solution to have multiple images for your company admin you have to organize your relation like there will be one junction entity which will point to sonata media entity in a ManyToOne relation and also to your products entity in a ManyToOne relation as well i have created this type of collection for one of needs for footer widgets which can have multiple images so you can map it for your products images as well in a similar way.

Footer Entity contains a property named as links which points to a junction entity FooterWidgetsHasMedia in OneToMany way,junction entity (FooterWidgetsHasMedia ) holds the relation to sonata media in addition i need multiple images for my each footer object and also for each image a in need a hover image too so my junction entity basically holds two properties that points to sonata media

FooterWidgets

/**
 * @AssertNotBlank()
 * @ORMOneToMany(targetEntity="TrafficWidgetsBundleEntityFooterWidgetsHasMedia", mappedBy="footerWidget",cascade={"persist","remove"} )
 */
protected $links;


/**
 * Remove widgetImages
 *
 * @param ApplicationSonataMediaBundleEntityMedia $widgetImages
 */
public function removeLinks(TrafficWidgetsBundleEntityFooterWidgetsHasMedia $links)
{
    $this->links->removeElement($links);
}


/**
 * Get widgetImages
 *
 * @return DoctrineCommonCollectionsCollection
 */
public function getLinks()
{
    return $this->links;
}


/**
 * {@inheritdoc}
 */
public function setLinks($links)
{
    $this->links = new ArrayCollection();


    foreach ($links as $footerWidget) {
        $this->addLinks($footerWidget);
    }
}

/**
 * {@inheritdoc}
 */
public function addLinks(TrafficWidgetsBundleEntityFooterWidgetsHasMedia $links)
{
    $links->setFooterWidget($this);


    $this->links[] = $links;
}

Now my junction entity will points back to FooterWidgets and sonata media entity

FooterWidgetsHasMedia

Definitions of properties

/**
 * @var ApplicationSonataMediaBundleEntityMedia
 * @AssertNotBlank()
 * @ORMManyToOne(targetEntity="ApplicationSonataMediaBundleEntityMedia", cascade={"persist"}, fetch="LAZY")
 * @ORMJoinColumn(name="media_id", referencedColumnName="id")
 */
protected $media;

/**
 * @var ApplicationSonataMediaBundleEntityMedia
 * @AssertNotBlank()
 * @ORMManyToOne(targetEntity="ApplicationSonataMediaBundleEntityMedia", cascade={"persist"}, fetch="LAZY")
 * @ORMJoinColumn(name="media_hover_id", referencedColumnName="id")
 */
protected $mediaHover;

/**
 * @var TrafficWidgetsBundleEntityFooterWidgets
 * @AssertNotBlank()
 * @ORMManyToOne(targetEntity="TrafficWidgetsBundleEntityFooterWidgets", cascade={"persist","remove"} ,inversedBy="links", fetch="LAZY" )
 * @ORMJoinColumn(name="footer_widget_id", referencedColumnName="id",nullable=true)
 */
protected $footerWidget;
/**
 * @var integer
 * @ORMColumn(name="position", type="integer")
 */
protected $position;


/**
 * @var boolean
 * @ORMColumn(name="enable", type="boolean")
 */
protected $enabled;

Generate getters and setters for above properties

Now you have to create new admin for your collection which references to junction entity FooterWidgetsHasMedia and configureFormFields will look something like below

FooterWidgetsHasMediaAdmin

protected function configureFormFields(FormMapper $formMapper)
{
    $link_parameters = array();

    if ($this->hasParentFieldDescription()) {
        $link_parameters = $this->getParentFieldDescription()->getOption('link_parameters', array());
    }

    if ($this->hasRequest()) {
        $context = $this->getRequest()->get('context', null);

        if (null !== $context) {
            $link_parameters['context'] = $context;
        }
    }

    $formMapper

        ->add('media', 'sonata_type_model_list', array('required' => false), array(
            'link_parameters' => $link_parameters
        ))
        ->add('mediaHover', 'sonata_type_model_list', array('required' => false), array(
            'link_parameters' => $link_parameters
        ))
        ->add('enabled', null, array('required' => false))
        ->add('link', 'text', array('required' => false))
        ->add('position', 'hidden')


    ;
}

And your company admin will have a new field in configureFormFields

FooterWidgetsAdmin

        ->add('links', 'sonata_type_collection', array(
                'cascade_validation' => false,
                'type_options' => array('delete' => false),
            ), array(

                'edit' => 'inline',
                'inline' => 'table',
                'sortable' => 'position',
                'link_parameters' => array('context' => 'widgets'),
                'admin_code' => 'sonata.admin.footer_widgets_has_media' /*here provide service name for junction admin */
            )
        )

Register admin service for your new admin as

sonata.admin.footer_widgets_has_media:
    class: TrafficWidgetsBundleAdminFooterWidgetsHasMediaAdmin
    tags:
        - { name: sonata.admin, manager_type: orm, group: "Widgets", label: "Footer Widgets Section Media" , show_in_dashboard: false }
    arguments:
        - ~
        - TrafficWidgetsBundleEntityFooterWidgetsHasMedia
        - ~
    calls:
        - [ setTranslationDomain, [TrafficWidgetsBundle]]

Demo snap shot

enter image description here

You can find full code demo here Git Hub hope it makes sense


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...