Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Collection Upload] Undefined variable: editable #12

Open
ksn135 opened this issue Nov 30, 2014 · 18 comments
Open

[Collection Upload] Undefined variable: editable #12

ksn135 opened this issue Nov 30, 2014 · 18 comments

Comments

@ksn135
Copy link
Contributor

ksn135 commented Nov 30, 2014

I'm on dev-master.
In vendor/symfony2admingenerator/form-extensions-bundle/Admingenerator/FormExtensionsBundle/Form/EventListener/CollectionUploadSubscriber.php at line 192

My config:

        files:
            label:                     Files
            dbType:               collection
            formType:             s2a_collection_upload
            addFormOptions:
                primary_key:          id
                nameable:             false
                nameable_field:       name
                sortable:             true
                sortable_field:       position
                editable:             [ description ]    
                type:               \Ksn135\CompanyBundle\Form\Type\Cabinet_file\EditType
                loadImageFileTypes:   /^image\/(gif|jpe?g|png)$/i
                previewMaxWidth:      100
                previewMaxHeight:     100
                previewAsCanvas:      true
                prependFiles:         false
                allow_add:            true
                allow_delete:         true
                error_bubbling:       false
                options:
                    data_class:       Ksn135\CompanyBundle\Entity\CabinetFile

My entity (almost the same as from example: https://github.com/symfony2admingenerator/FormExtensionsBundle/blob/master/Resources/doc/collection-upload/overview.md):

/**
 * @ORM\Table(name="cabinet_file")
 * @ORM\Entity(repositoryClass="Gedmo\Sortable\Entity\Repository\SortableRepository")
 * @Vich\Uploadable
 */
class CabinetFile implements UploadCollectionFileInterface
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @Vich\UploadableField(mapping="files", fileNameProperty="path")
     */
    protected $file;

    /**
     * @ORM\Column(type="string", length=255, nullable=true)
     */
    protected $path;

    /**
     * (Optional) nameable field
     *
     * @ORM\Column(type="string", length=255, nullable=true)
     */
    protected $name;

    /**
     * (Optional) additional editable field
     *
     * @ORM\Column(type="string", length=255, nullable=true)
     */
    protected $description;

    /**
     * @ORM\Column(name="category_id", type="integer")
     */
    private $categoryId;

    /**
     * (Optional) sortable group
     *
     * @Gedmo\SortableGroup
     * @ORM\ManyToOne(targetEntity="CabinetCategory", inversedBy="files")
     * @ORM\JoinColumn(name="category_id", referencedColumnName="id")
     */
    protected $category;

    /**
     * (Optional) sortable position
     *
     * @Gedmo\SortablePosition
     * @ORM\Column(name="position", type="integer")
     */
    protected $position;



    public function setFile(\Symfony\Component\HttpFoundation\File\File $file) {
        $this->file = $file;
        return $this;
    }

    public function getFile() {
        return $this->file;
    }

    public function getId() {
        return $this->id;
    }

    public function getCategoryId() {
        return $this->categoryId;
    }

    public function getPath() {
        return $this->path;
    }

    public function getName() {
        return $this->name;
    }

    public function getPosition() {
        return $this->position;
    }

    public function getDescription() {
        return $this->description;
    }

    public function getSize() {
        return $this->file->getFileInfo()->getSize();
    }

    public function setParent($parent) {
        $this->setCategory($parent);
    }

    public function getPreview() {
        return (preg_match('/image\/.*/i', $this->file->getMimeType()));
    }


    /**
     * Set path
     *
     * @param string $path
     * @return CabinetFile
     */
    public function setPath($path)
    {
        $this->path = $path;

        return $this;
    }

    /**
     * Set name
     *
     * @param string $name
     * @return CabinetFile
     */
    public function setName($name)
    {
        $this->name = $name;

        return $this;
    }

    /**
     * Set description
     *
     * @param string $description
     * @return CabinetFile
     */
    public function setDescription($description)
    {
        $this->description = $description;

        return $this;
    }

    /**
     * Set position
     *
     * @param integer $position
     * @return CabinetFile
     */
    public function setPosition($position)
    {
        $this->position = $position;

        return $this;
    }

    /**
     * Get category
     *
     * @return \Ksn135\CompanyBundle\Entity\CabinetCategory 
     */
    public function getCategory()
    {
        return $this->category;
    }
}
@sescandell
Copy link
Member

Hi @ksn135

This is probably because you don't go through https://github.com/symfony2admingenerator/FormExtensionsBundle/blob/master/Form/EventListener/CollectionUploadSubscriber.php#L174

I don't have immediately time to check for it. Maybe later today or next week (working on AdminLTE in priority :) ). But if you can find what's wrong and make a PR, that would be great !

Question: do you have the same issue with the "legacy" bundle?
I have a sample in some of my codes, I'll try to show it to you ASAP

@ksn135
Copy link
Contributor Author

ksn135 commented Nov 30, 2014

What is the "legacy" bundle ? You mean AvocodeFormExtensionsBundle ?

@sescandell
Copy link
Member

Yes... the current one is still unstable and in active development

@ksn135
Copy link
Contributor Author

ksn135 commented Nov 30, 2014

My bad, production project already running on it
Well, very good motivated hands avaiable to work on issues to speed up stabilisation!!! ))

@ksn135
Copy link
Contributor Author

ksn135 commented Nov 30, 2014

Well, I try to get it fixed by myself

@sescandell
Copy link
Member

Here is some CollectionUpload I'm using in a production project working well:

$builder->add('medias', 'afe_collection_upload', array(
                    'primary_key' => 'pathHashed',
                    'label' => false,
                    'sortable' => false,
                    'nameable' => false,
                    'editable' => $options['with_description'] ? array('description') : array(),
                    'allow_add' => true,
                    'allow_delete' => true,
                    'type' => 'my_project_media',
                    'uploadRouteName' => 'async_upload',
                    'autoUpload' => $options['auto_upload'],
                    'options' => array(
                        'data_class' => Media::class,
                        'media_type' => Media::PICTURE,
                        'for_library' => true,
                        'with_description' => $options['with_description']
                    )
                ));

Could you please show us the generated Form?

Thank you,

@ksn135
Copy link
Contributor Author

ksn135 commented Nov 30, 2014

Thank you very much for your help!
Curently it starts working. I'm testing it.
Code changes in CollectionUploadSubscriber.php:

        if ($this->allow_add) {
            // create file entites for each file
            foreach ($this->uploads as $upload) {
                $editable = $this->editable; // ADD THIS LINE
                if (!is_object($upload) && !is_null($this->storage)) {
                    // read submitted editable
                    // $editable = $this->editable[$upload];  // COMMENT OUT THIS ONE
                    $upload = $this->storage->getFile($upload);
                }

And my form class:

<?php
namespace Ksn135\CompanyBundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Form\FormBuilderInterface;
use JMS\DiExtraBundle\Annotation\FormType;

/**
 * @FormType
 */
class CabinetFileType extends AbstractType
{
    /**
     * (non-PHPdoc)
     * @see \Symfony\Component\Form\AbstractType::buildForm()
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('id', 'hidden')
            ->add('name', 'text', array( 'label' => 'Название'))
            ->add('description', 'text', array( 'label' => 'Описание'))
            ->add('position', 'hidden')
            ->add('file','file');
    }
    /**
     * (non-PHPdoc)
     * @see \Symfony\Component\Form\AbstractType::setDefaultOptions()
     */
    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
       $resolver->setDefaults(array(
               'data_class' => 'Ksn135\CompanyBundle\Entity\CabinetFile',
                'groups' => array(),
            'cascade_validation' => true
       ));
        $resolver->setAllowedTypes(array(
            'groups' => 'array',
        ));
    }
    /* (non-PHPdoc)
     * @see \Symfony\Component\Form\FormTypeInterface::getName()
     */
    public function getName()
    {
        return 'cabinet_file_type';
    }

}

@sescandell
Copy link
Member

This is your CabinetFileType, could you please show us the generated CabinetType (I assumed) form.
Regarding your fix, take care that you change a big behavior there... Could you please double check what the type of $this->editable is? (is it an array or something else?)

I assume you are not in a "async" mode, am I right?

So basically, your fix might be wrong in async mode for example (I'm not sure, there is a long time I worked on that part, I could double check what I'm saying if you want). I think the right fix should be something like:

if ($this->allow_add) {
            // create file entites for each file
           $editable = array(); // Prevent strange behavior... or don't define it and so Exception will be thrown meaning the user mis-configured its form?
            foreach ($this->uploads as $index => $upload) {
                if (!is_object($upload) && !is_null($this->storage)) {
                    // read submitted editable
                    $editable = $this->editable[$upload];
                    $upload = $this->storage->getFile($upload);
                } elseif (array_key_exists($index, $this->editable)) {
                  $editable = $this->editable[$index];
                }

Not sure at all about it.
You can also take a look here: https://github.com/sescandell/CollectionUploadSample

@sescandell
Copy link
Member

My mistake, in my sample project, I'm not using the editable param... I should make an example...

@ksn135
Copy link
Contributor Author

ksn135 commented Nov 30, 2014

I assume you are not in a "async" mode, am I right?

With my fix it works in both modes.

If you return back my uncommented line you've got undefined index error,
because $upload is null when you save already filed form.

My autogenerated form type for CabinetCategory:

<?php

namespace Admingenerated\Ksn135CompanyBundle\Form\BaseCabinet_categoryType;

use Admingenerator\GeneratorBundle\Form\BaseType;
use Admingenerator\GeneratorBundle\Form\BaseOptions;
use Symfony\Component\Form\FormBuilderInterface;
use JMS\SecurityExtraBundle\Security\Authorization\Expression\Expression;

class EditType extends BaseType
{
    protected $securityContext;

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $this->groups = $options['groups'];

            if ($this->isDisplayedTitle()) {
            $builder->add('title', $this->getTypeTitle(), $this->getOptionsTitle($options));
        }

            if ($this->isDisplayedSlug()) {
            $builder->add('slug', $this->getTypeSlug(), $this->getOptionsSlug($options));
        }

            if ($this->isDisplayedFiles()) {
            $builder->add('files', $this->getTypeFiles(), $this->getOptionsFiles($options));
        }

        }

    /**
     * Get form type for title field.
     *
     * @return string|FormTypeInterface Field form type.
     */
    protected function getTypeTitle()
    {
        return $this->inject('text');
    }

    /**
     * Get options for title field.
     *
     * @param  array $builderOptions The builder options.
     * @return array Field options.
     */
    protected function getOptionsTitle(array $builderOptions = array())
    {
        $optionsClass = 'Ksn135\CompanyBundle\Form\Type\Cabinet_category\Options';
        $options = class_exists($optionsClass) ? new $optionsClass() : null;

        return $this->resolveOptions('title', array(  'required' => true,  'label' => 'Наименование',  'translation_domain' => 'Admin',), $builderOptions, $options);
    }

    /**
     * Check groups for title field.
     *
     * @return boolean
     */
    protected function isDisplayedTitle()
    {
        return $this->checkGroups(array());
    }

    /**
     * Get form type for slug field.
     *
     * @return string|FormTypeInterface Field form type.
     */
    protected function getTypeSlug()
    {
        return $this->inject('text');
    }

    /**
     * Get options for slug field.
     *
     * @param  array $builderOptions The builder options.
     * @return array Field options.
     */
    protected function getOptionsSlug(array $builderOptions = array())
    {
        $optionsClass = 'Ksn135\CompanyBundle\Form\Type\Cabinet_category\Options';
        $options = class_exists($optionsClass) ? new $optionsClass() : null;

        return $this->resolveOptions('slug', array(  'required' => true,  'label' => 'Ссылка',  'translation_domain' => 'Admin',), $builderOptions, $options);
    }

    /**
     * Check groups for slug field.
     *
     * @return boolean
     */
    protected function isDisplayedSlug()
    {
        return $this->checkGroups(array());
    }

    /**
     * Get form type for files field.
     *
     * @return string|FormTypeInterface Field form type.
     */
    protected function getTypeFiles()
    {
        return $this->inject('s2a_collection_upload');
    }

    /**
     * Get options for files field.
     *
     * @param  array $builderOptions The builder options.
     * @return array Field options.
     */
    protected function getOptionsFiles(array $builderOptions = array())
    {
        $optionsClass = 'Ksn135\CompanyBundle\Form\Type\Cabinet_category\Options';
        $options = class_exists($optionsClass) ? new $optionsClass() : null;

        return $this->resolveOptions('files', array(  'required' => false,  'primary_key' => 'id',  'nameable' => true,  'nameable_field' => 'name',  'sortable' => true,  'sortable_field' => 'position',  'editable' =>   array(    0 => 'description',  ),  'type' => new \Ksn135\CompanyBundle\Form\Type\CabinetFileType(),  'autoUpload' => true,  'loadImageFileTypes' => '/^image\\/(gif|jpe?g|png)$/i',  'previewMaxWidth' => 100,  'previewMaxHeight' => 100,  'previewAsCanvas' => true,  'prependFiles' => false,  'allow_add' => true,  'allow_delete' => true,  'error_bubbling' => false,  'options' =>   array(    'data_class' => 'Ksn135\\CompanyBundle\\Entity\\CabinetFile',  ),  'label' => 'Загруженные файлы',  'translation_domain' => 'Admin',), $builderOptions, $options);
    }

    /**
     * Check groups for files field.
     *
     * @return boolean
     */
    protected function isDisplayedFiles()
    {
        return $this->checkGroups(array());
    }


    public function getName()
    {
        return 'edit_companybundle_cabinetcategory';
    }
}

@sescandell
Copy link
Member

OK... I'll take a look later, if it works for now for you, it's okay 👍

@ksn135
Copy link
Contributor Author

ksn135 commented Nov 30, 2014

No, async mode IS NOT working in my case. It's problem with folder permissions in MAC OS X: http://stackoverflow.com/questions/16378828/result-of-sys-get-temp-dir-not-writable-by-apache-processs-user .
Sync mode is worked after my patch, but now I can't delete uploaded file. Form submited and filed back again with just deleted file. Continue investigation... )

@ksn135
Copy link
Contributor Author

ksn135 commented Nov 30, 2014

Well, javascript just remove uploaded file row from DOM on "delete" button and then on "save" send empty form. CollectionUploadSubscriber can't do anything because it dosen't recieve expected "delete_uploads" key in empty data array. Moving on... May be just piece of Javascript code just dissapers ? Or may be it's again something wrong in my case, don't know /

@sescandell
Copy link
Member

Did you try the other bundle: https://github.com/symfony2admingenerator/AvocodeFormExtensionsBundle ?

@rpostolov
Copy link

Hi,

I have the same problem with editable :

Undefined variable: editable

Was it fixed ?

Thanks

@nicoip
Copy link

nicoip commented Aug 31, 2015

Hi,

I have the same problem too, with field editable :
Undefined variable: editable

Thanks !

@tknuppe
Copy link
Contributor

tknuppe commented Nov 11, 2015

I have encountered the same issue. My fix is as follows (Starting in line 174 in CollectionUploadSubscriber.php):


                $editable = array();
                if (!is_object($upload) && !is_null($this->storage)) {
                    // read submitted editable
                    $editable[] = array_key_exists($upload, $this->editable) ? $this->editable[$upload] : array();
                    $upload = $this->storage->getFile($upload);
                }

I don't know exactly how this works but I assumed that on first upload (synchronous) there are objects in $upload and then the variable §editable will never be set. And when it is set, it has to be an array.

Please fix this soon. I would do this on my own but I'm not quite sure if my solution is best. It works though (at least for me but there is a chance to break async mode with my modifiaction) ;)

In fact $editable has to be a multidimensional array (see foreach call), maybe there is some more work to do to clean it up.

Greetings.

@ksn135
Copy link
Contributor Author

ksn135 commented Apr 10, 2016

@tknuppe For me your code works with the following changes:
$editable = array_key_exists($upload, $this->editable) ? $this->editable[$upload] : array();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants