0

就是这样,我在表格上上传照片时遇到问题。我可以使用 Doctrine 将表单保存到 SQL 中,但是当我想使用 VichUploaderBundle 保存一个或多个图像时,会出现以下错误消息:

无法确定“MO\WebAppBundle\Entity\WebApp”类中属性“photos”的访问类型。

这是我的代码:

我的第一个实体:

<?php

namespace MO\WebAppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\File;
use Vich\UploaderBundle\Mapping\Annotation as Vich;

/**
 * Photo
 *
 * @ORM\Table()
 * @ORM\Entity(repositoryClass="MO\WebAppBundle\Repository\PhotoRepository")
 * @Vich\Uploadable
 */
class Photo
{
    /**
     * @ORM\ManyToOne(targetEntity="MO\WebAppBundle\Entity\WebApp")
     * @ORM\JoinColumn(nullable=false)
     */
    private $travel;

    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @ORM\Column(type="text")
     */
    private $image;

    /**
     * @ORM\Column(type="text")
     */
    private $imagenom;

    /**
     * @ORM\Column(type="datetime")
     *
     * @var \Datetime
     */
    private $updatedAt;

    /**
     * @ORM\Column(type="text")
     */
    private $imagedescription;

    /**
     * @Vich\UploadableField(mapping="photo_images", fileNameProperty="image")
     * @var File
     */
    private $imageFile;

    /**
     * @ORM\ManyToOne(targetEntity="WebApp", inversedBy="photos")
     */
    private $webapp;

    /**
     * Set travel
     *
     * @param \MO\WebAppBundle\Entity\WebApp $travel
     *
     * @return Photo
     */
    public function setTravel(\MO\WebAppBundle\Entity\WebApp $travel)
    {
        $this->travel = $travel;

        return $this;
    }

    /**
     * Get travel
     *
     * @return \MO\WebAppBundle\Entity\WebApp
     */
    public function getTravel()
    {
        return $this->travel;
    }

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


    public function setImage($image)
    {
        $this->image = $image;
    }

    public function getImage()
    {
        return $this->image;
    }


    public function getImageFile()
    {
        return $this->imageFile;
    }

    public function setImageFile(File $image = null)
    {
        $this->imageFile = $image;

        // VERY IMPORTANT:
        // It is required that at least one field changes if you are using Doctrine,
        // otherwise the event listeners won't be called and the file is lost
        if ($image) {
            // if 'updatedAt' is not defined in your entity, use another property
            $this->updatedAt = new \DateTime('now');
        }
    }

    /**
     * @return mixed
     */
    public function getWebApp()
    {
        return $this->webapp;
    }

    /**
     * @param mixed $webapp
     */
    public function setPage($webapp)
    {
        $this->webapp = $webapp;
    }


    function __toString()
    {
        $val=$this->id.' - '.$this->imagenom;
        return $val;
    }


    /**
     * Set imagenom
     *
     * @param string $imagenom
     *
     * @return Photo
     */
    public function setImagenom($imagenom)
    {
        $this->imagenom = $imagenom;

        return $this;
    }

    /**
     * Get imagenom
     *
     * @return string
     */
    public function getImagenom()
    {
        return $this->imagenom;
    }

    /**
     * Set updatedAt
     *
     * @param \DateTime $updatedAt
     *
     * @return Photo
     */
    public function setUpdatedAt($updatedAt)
    {
        $this->updatedAt = $updatedAt;

        return $this;
    }

    /**
     * Get updatedAt
     *
     * @return \DateTime
     */
    public function getUpdatedAt()
    {
        return $this->updatedAt;
    }

    /**
     * Set imagedescription
     *
     * @param string $imagedescription
     *
     * @return Photo
     */
    public function setImagedescription($imagedescription)
    {
        $this->imagedescription = $imagedescription;

        return $this;
    }

    /**
     * Get imagedescription
     *
     * @return string
     */
    public function getImagedescription()
    {
        return $this->imagedescription;
    }

}

我的第二个实体:

<?php

namespace MO\WebAppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
//pour le slug
use Gedmo\Mapping\Annotation as Gedmo;


/**
 * WebApp
 *
 * @ORM\Table(name="mo_travel")
 * @ORM\Entity(repositoryClass="MO\WebAppBundle\Repository\WebAppRepository")
 *
 */
class WebApp
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;


  /**
     * @ORM\OneToMany(targetEntity="Photo", mappedBy="webapp")
     */
    private $photos;

    /**
     * @var \DateTime
     *
     * @ORM\Column(name="date", type="datetime")
     */
    private $date;

    /**
     * @var string
     *
     * @ORM\Column(name="title", type="string", length=255)
     */
    private $title;

    /**
     * @var string
     *
     * @ORM\Column(name="author", type="string", length=255)
     */
    private $author;

    /**
     * @var string
     *
     * @ORM\Column(name="content", type="text")
     */
    private $content;

    /**
     * @ORM\Column(name="published", type="boolean")
     */
    private $published = true;

    /**
     * @ORM\ManyToMany(targetEntity="MO\WebAppBundle\Entity\Category", cascade={"persist"})
     * @ORM\JoinTable(name="mo_travel_category")
     */
    private $categories;

    /**
     * @ORM\Column(name="updated_at", type="datetime", nullable=true)
     *
     * @var \DateTime
     */
    private $updatedAt;

    /**
     * @Gedmo\Slug(fields={"title"})
     * @ORM\Column(name="slug", type="string", length=255, unique=true)
     */
    private $slug;

    /**
     * WebApp constructor.
     */
    public function __construct()
    {
        // Par défaut, la date de l'annonce est la date d'aujourd'hui
        $this->date = new \Datetime();
        $this->categories = new ArrayCollection();
        $this->images = new ArrayCollection();
    }

    /**
     * @ORM\PreUpdate
     */
    public function updateDate()
    {
        $this->setUpdatedAt(new \Datetime());
    }

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

    /**
     * Set date
     *
     * @param \DateTime $date
     *
     * @return WebApp
     */
    public function setDate($date)
    {
        $this->date = $date;

        return $this;
    }

    /**
     * Get date
     *
     * @return \DateTime
     */
    public function getDate()
    {
        return $this->date;
    }

    /**
     * Set title
     *
     * @param string $title
     *
     * @return WebApp
     */
    public function setTitle($title)
    {
        $this->title = $title;

        return $this;
    }

    /**
     * Get title
     *
     * @return string
     */
    public function getTitle()
    {
        return $this->title;
    }

    /**
     * Set author
     *
     * @param string $author
     *
     * @return WebApp
     */
    public function setAuthor($author)
    {
        $this->author = $author;

        return $this;
    }

    /**
     * Get author
     *
     * @return string
     */
    public function getAuthor()
    {
        return $this->author;
    }

    /**
     * Set content
     *
     * @param string $content
     *
     * @return WebApp
     */
    public function setContent($content)
    {
        $this->content = $content;

        return $this;
    }

    /**
     * Get content
     *
     * @return string
     */
    public function getContent()
    {
        return $this->content;
    }

    /**
     * @param boolean $published
     *
     * @return WebApp
     */
    public function setPublished($published)
    {
        $this->published = $published;
    }

    /**
     * @return mixed
     */
    public function getPublished()
    {
        return $this->published;
    }

    /**
     * Add category
     *
     * @param \MO\WebAppBundle\Entity\Category $category
     *
     * @return WebApp
     */
    public function addCategory(\MO\WebAppBundle\Entity\Category $category)
    {
        $this->categories[] = $category;

        return $this;
    }

    /**
     * Remove category
     *
     * @param \MO\WebAppBundle\Entity\Category $category
     */
    public function removeCategory(\MO\WebAppBundle\Entity\Category $category)
    {
        $this->categories->removeElement($category);
    }

    /**
     * Get categories
     *
     * @return \Doctrine\Common\Collections\Collection
     */
    public function getCategories()
    {
        return $this->categories;
    }

    /**
     * @return mixed
     */
    public function getUpdatedAt()
    {
        return $this->updatedAt;
    }

    /**
     * @param mixed $updatedAt
     *
     * @return WebApp
     */
    public function setUpdatedAt($updatedAt)
    {
        $this->updatedAt = $updatedAt;
    }

    /**
     * Set slug
     *
     * @param string $slug
     *
     * @return WebApp
     */
    public function setSlug($slug)
    {
        $this->slug = $slug;

        return $this;
    }

    /**
     * Get slug
     *
     * @return string
     */
    public function getSlug()
    {
        return $this->slug;
    }

    /*----------------------------------*/
    /**
     * Add photo
     *
     * @param \MO\WebAppBundle\Entity\Photo $photo
     *
     * @return WebApp
     */
    public function addPhoto(\MO\WebAppBundle\Entity\Photo $photo)
    {
        $this->photos[] = $photo;

        return $this;
    }

    /**
     * Remove photo
     *
     * @param \MO\WebAppBundle\Entity\Photo $photo
     */
    public function removePhoto(\MO\WebAppBundle\Entity\Photo $photo)
    {
        $this->photos->removeElement($photo);
    }

    /**
     * Get photos
     *
     * @return \Doctrine\Common\Collections\Collection
     */
    public function getPhotos()
    {
        return $this->photos;
    }

}

我的第一种形式:

<?php

namespace MO\WebAppBundle\Form;

use MO\WebAppBundle\Entity\Image;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;

class WebAppType extends AbstractType
{
    /**
     * Réutilisabilité de notre formulaire
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('date',      DateTimeType::class)
            ->add('title',     TextType::class)
            ->add('author',    TextType::class)
            ->add('content',   TextareaType::class)
            ->add('photos', PhotoType::class)


            //->add('images',     ImageType::class)

            /*->add('images', FileType::class, array(
                'multiple'     => true,
                'label' => false,
                'label_attr' => array('class' => 'MOWebAppBundle:Image'),
                'required' => true,
            ))


             /*
              * Rappel :
              ** - 1er argument : nom du champ, ici « categories », car c'est le nom de l'attribut
              ** - 2e argument : type du champ, ici « CollectionType » qui est une liste de quelque chose
              ** - 3e argument : tableau d'options du champ
              */
            ->add('categories', EntityType::class, array(
                'class'        => 'MOWebAppBundle:Category',
                'choice_label' => 'name',
                'expanded'     => true,
                'multiple'     => true,
            ))
            ->add('save',      SubmitType::class);

        // On ajoute une fonction qui va écouter un évènement de formulaire
        $builder->addEventListener(
            FormEvents::PRE_SET_DATA,    // 1er argument : L'évènement qui nous intéresse : ici, PRE_SET_DATA
            function(FormEvent $event) { // 2e argument : La fonction à exécuter lorsque l'évènement est déclenché
                // On récupère notre objet WebApp (travel) sous-jacent
                $travel = $event->getData();

                // Cette condition est importante, on en reparle plus loin
                if (null === $travel) {
                    return; // On sort de la fonction sans rien faire lorsque $advert vaut null
                }

                // Si l'annonce n'est pas publiée, ou si id null
                if (!$travel->getPublished() || null === $travel->getId()) {
                    // Alors on ajoute le champ published
                    $event->getForm()->add('published', CheckboxType::class, array('required' => false));
                } else {
                    // Sinon, on le supprime
                    $event->getForm()->remove('published');
                }
            }
        );
    }

    /**
     * {@inheritdoc}
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'MO\WebAppBundle\Entity\WebApp'
        ));
    }
}

我的第二种形式:

<?php

namespace MO\WebAppBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Vich\UploaderBundle\Form\Type\VichFileType;


class PhotoType extends AbstractType
{
    /**
     * @param FormBuilderInterface $builder
     * @param array $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            //->add('name')
            ->add('imageFile', VichFileType::class, array(
                'required'      => false,
                'allow_delete'  => true, // not mandatory, default is true
                'download_link' => true, // not mandatory, default is true
            ));
    }

    /**
     * {@inheritdoc}
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'MO\WebAppBundle\Entity\Photo',
        ));
    }

}

我的表格树枝:

 {{ form_start(form, {'attr': {'class': 'form-horizontal'}}) }}
...
  {{ form_row(form.photos) }}
...
{{ form_end(form) }}

我的 config.yml :

imports:
    - { resource: parameters.yml }
    - { resource: security.yml }
    - { resource: services.yml }

# Put parameters here that don't need to change on each machine where the app is deployed
# https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration
parameters:
    locale: fr
    app.path.photo_images: /uploads/img
    image_directory: /uploads/img

framework:
    #esi: ~
    #translator: { fallbacks: ['%locale%'] }
    secret: '%secret%'
    router:
        resource: '%kernel.project_dir%/app/config/routing.yml'
        strict_requirements: ~
    form: ~
    csrf_protection: ~
    validation: { enable_annotations: true }
    #serializer: { enable_annotations: true }
    templating:
        engines: ['twig']
    default_locale: '%locale%'
    trusted_hosts: ~
    session:
        # https://symfony.com/doc/current/reference/configuration/framework.html#handler-id
        handler_id: session.handler.native_file
        save_path: '%kernel.project_dir%/var/sessions/%kernel.environment%'
    fragments: ~
    http_method_override: true
    assets: ~
    php_errors:
        log: true

# Twig Configuration
twig:
    debug: '%kernel.debug%'
    strict_variables: '%kernel.debug%'

# Doctrine Configuration
doctrine:
    dbal:
        driver: pdo_mysql
        host: '%database_host%'
        port: '%database_port%'
        dbname: '%database_name%'
        user: '%database_user%'
        password: '%database_password%'
        charset: UTF8
        # if using pdo_sqlite as your database driver:
        #   1. add the path in parameters.yml
        #     e.g. database_path: "%kernel.project_dir%/var/data/data.sqlite"
        #   2. Uncomment database_path in parameters.yml.dist
        #   3. Uncomment next line:
        #path: '%database_path%'

    orm:
        auto_generate_proxy_classes: '%kernel.debug%'
        naming_strategy: doctrine.orm.naming_strategy.underscore
        auto_mapping: true

# Swiftmailer Configuration
swiftmailer:
    transport: '%mailer_transport%'
    host: '%mailer_host%'
    username: '%mailer_user%'
    password: '%mailer_password%'
    spool: { type: memory }

# Stof\DoctrineExtensionsBundle configuration
stof_doctrine_extensions:
    orm:
        default:
            sluggable: true

# VichUploaderBundle
vich_uploader:
    db_driver: orm
    mappings:
            photo_images:
                uri_prefix:         /uploads/img
                upload_destination: '%kernel.root_dir%/../web/uploads/img'
                namer:              vich_uploader.namer_uniqid

                inject_on_load:     true
                delete_on_update:   true
                delete_on_remove:   true

我的 AppKernel.php:

<?php

use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Config\Loader\LoaderInterface;

class AppKernel extends Kernel
{
    //pour éviter l'erreur dans bin/console "Warning: date_default_timezone_get()"
    public function __construct($environment, $debug)
    {
        date_default_timezone_set('Europe/Paris');
        parent::__construct($environment, $debug);
    }

    public function registerBundles()
    {
        $bundles = [
            new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
            new Symfony\Bundle\SecurityBundle\SecurityBundle(),
            new Symfony\Bundle\TwigBundle\TwigBundle(),
            new Symfony\Bundle\MonologBundle\MonologBundle(),
            new Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle(),
            new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(),
            new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(),
            //mon application
            new Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle(),
            new MO\WebAppBundle\MOWebAppBundle(),
            new MO\CoreBundle\MOCoreBundle(),
            //images multiples
            new EasyCorp\Bundle\EasyAdminBundle\EasyAdminBundle,
            new Vich\UploaderBundle\VichUploaderBundle(),
        ];

        if (in_array($this->getEnvironment(), ['dev', 'test'], true)) {
            $bundles[] = new Symfony\Bundle\DebugBundle\DebugBundle();
            $bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle();
            $bundles[] = new Sensio\Bundle\DistributionBundle\SensioDistributionBundle();
            $bundles[] = new Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle();

            if ('dev' === $this->getEnvironment()) {
                $bundles[] = new Sensio\Bundle\GeneratorBundle\SensioGeneratorBundle();
                $bundles[] = new Symfony\Bundle\WebServerBundle\WebServerBundle();
            }
        }

        return $bundles;
    }

    public function getRootDir()
    {
        return __DIR__;
    }

    public function getCacheDir()
    {
        return dirname(__DIR__).'/var/cache/'.$this->getEnvironment();
    }

    public function getLogDir()
    {
        return dirname(__DIR__).'/var/logs';
    }

    public function registerContainerConfiguration(LoaderInterface $loader)
    {
        $loader->load($this->getRootDir().'/config/config_'.$this->getEnvironment().'.yml');
    }
}

我的控制器:

$travel = new WebApp();
$form = $this->createForm(WebAppType::class, $travel);

$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
    $em = $this->getDoctrine()->getManager();
    $em->persist($travel);
    $em->flush();

    return $this->redirectToRoute('mo_webapp_travel', array('id' => $travel->getId()));
}

return $this->render('MOWebAppBundle:WebApp:add.html.twig', array(
    'form' => $form->createView(),
));

如果有人来帮助我,我将非常感激...

4

1 回答 1

1

您需要将photos字段的表单类型声明为Collection

$builder
    ...
    ->add('photos', CollectionType::class, array(
        'entry_type' => PhotoType::class
    ));
于 2017-10-26T05:47:32.713 回答