Хобрук: Ваш путь к мастерству в программировании

VichUploaderBundle не сохраняет поле с именем файла NULL

У меня есть приложение, которое позволяет пользователям загружать резюме при подаче заявки на курсы. Форма ввода возвращается и отображается без ошибки. При отправке формы ошибка не выдается. Загруженный файл находится в данных POST - как экземпляр UploadedFile и поле cvUpdatedAt. Однако столбец, используемый для хранения имени файла, устанавливается в NULL, когда запись сохраняется в БД.

Вот моя Сущность:

<?php

namespace My\CamsBundle\Entity;

use DateTime;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\Validator\Constraints as Assert;
use Vich\UploaderBundle\Mapping\Annotation as Vich;

/**
 * @ORM\Entity(repositoryClass="My\CamsBundle\Entity\Repository\ApplicationRepository")
 * @ORM\Table(name="application")
 * @Vich\Uploadable
 * @ORM\HasLifecycleCallbacks()
 */
class Application {

  /**
   * @ORM\Id
   * @ORM\Column(type="integer")
   * @ORM\GeneratedValue(strategy="AUTO")
   */
  protected $id;

  /**
   * @Assert\File(
   *     maxSize="2M",
   * )
   * @Vich\UploadableField(mapping="cv_file", fileNameProperty="cvName")
   *
   * This is not a mapped field of entity metadata, just a simple property.
   *
   * @var File $cvFile
   */
  protected $cvFile;

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

  /**
   * @ORM\Column(type="datetime")
   *
   * @var \DateTime $cvUpdatedAt
   */
  protected $cvUpdatedAt;


  public function __construct() {  }

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

  /**
   * If manually uploading a file (i.e. not using Symfony Form) ensure an instance
   * of 'UploadedFile' is injected into this setter to trigger the  update. If this
   * bundle's configuration parameter 'inject_on_load' is set to 'true' this setter
   * must be able to accept an instance of 'File' as the bundle will inject one here
   * during Doctrine hydration.
   *
   * @param File|\Symfony\Component\HttpFoundation\File\UploadedFile $cv
   */
  public function setCvFile(File $cv = null) {
    $this->cvFile = $cv;

    if ($cv) {
      // 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
      $this->cvUpdatedAt = new \DateTime('now');
    }
  }

  /**
   * @return File
   */
  public function getCvFile() {
    return $this->cvFile;
  }

  /**
   * @param string $cvName
   */
  public function setCvName($cvName) {
    $this->cvName = $cvName;
  }

  /**
   * @return string
   */
  public function getCvName() {
    return $this->cvName;
  }

  /**
   * Set cvUpdatedAt
   *
   * @param \DateTime $cvUpdatedAt
   * @return Application
   */
  public function setCvUpdatedAt($cvUpdatedAt) {
    $this->cvUpdatedAt = $updatcvUpdatedAt;

    return $this;
  }

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

}

Вот форма:

<?php

namespace My\CamsBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

class ApplicationType extends AbstractType {

  public function buildForm(FormBuilderInterface $builder, array $options) {
    if (array_key_exists('action', $options)) {
      $builder->setAction($options['action']);
    }

    // See http://lrotherfield.com/blog/symfony2-forms-hidden-entity-type-part-2/
    $builder->add('cvFile', 'vich_file', array(
        'required'      => false,
        'allow_delete'  => true, // not mandatory, default is true
        'download_link' => true, // not mandatory, default is true
        'label'         => 'Upload CV',
      ));

    $builder->add('save', 'submit', array(
      'label' => 'Save',
      'attr' => array(
        'class' => 'btn btn-action'
      )
    ));

    // Basic query builder solution. Not much better than the documentation.
    // http://inchoo.net/dev-talk/symfony2-entity-field-type/
  }

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

  public function setDefaultOptions(OptionsResolverInterface $resolver) {
    $resolver->setDefaults(array(
      'data_class' => 'My\CamsBundle\Entity\Application',
    ));
  }

}

Вот конфиг:

imports:
    - { resource: parameters.yml }
    - { resource: security.yml }
    - { resource: @MyCamsBundle/Resources/config/config.yml }

framework:
    #esi:             ~
    #translator:      { fallback: "%locale%" }
    secret:          "%secret%"
    router:
        resource: "%kernel.root_dir%/config/routing.yml"
        strict_requirements: ~
    form:            ~
    csrf_protection: ~
    validation:      { enable_annotations: true }
    templating:
        engines: ['twig']
        #assets_version: SomeVersionScheme
    default_locale:  "%locale%"
    trusted_proxies: ~
    session:         ~
    fragments:       ~
    http_method_override: true

# Twig Configuration
twig:
    debug:            "%kernel.debug%"
    strict_variables: "%kernel.debug%"
    form:
        resources:
            - 'VichUploaderBundle:Form:fields.html.twig'


# Doctrine Configuration
doctrine:
    dbal:
        default_connection: default
        connections:
            default:
                driver:   "%database_driver%"
                host:     "%database_host%"
                port:     "%database_port%"
                dbname:   "%database_name%"
                user:     "%database_user%"
                password: "%database_password%"
                charset:  UTF8
            customer:
                driver:   "%database_driver2%"
                host:     "%database_host2%"
                port:     "%database_port2%"
                dbname:   "%database_name2%"
                user:     "%database_user2%"
                password: "%database_password2%"
                charset:  UTF8

    orm:
        auto_generate_proxy_classes: "%kernel.debug%"
        entity_managers:
            default:
                auto_mapping: true
                mappings:
                  MyCamsBundle: ~


vich_uploader:
    db_driver: orm

    mappings:
        cv_file:
            uri_prefix:         /uploads/documents
            upload_destination: %kernel.root_dir%/../web/uploads/documents
            #namer:              vich_uploader.namer_uniqid

            inject_on_load:     false
            delete_on_update:   true
            delete_on_remove:   true

Шаблон формы:

{% extends 'MyCamsBundle::layout.html.twig' %}
{% form_theme form 'MyCamsBundle:Form:fields.html.twig' %}

{% block body %}
<div class="main">
    {{ form_errors(form) }}
    <div class="row">
        <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
            <div class="heading">Application</div>
        </div>
    </div>


    <section id="application-form">
        {{ form_start(form) }}
    <div class="row well demonstrator-experience">
        <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 cv-details">

        <span class="btn btn-action btn-file">
            {{ form_label(form.cvFile) }} {{ form_widget(form.cvFile) }}
        </span>

        </div>

    <div class="row">
        <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 text-right">
        </div>
        {{ form_widget(form.save) }}
    </div>
    {{ form_end(form) }}
    </section>

</div>
{% endblock %}

Контроллер:

<?php

namespace My\CamsBundle\Controller;

use DateTime;
use Doctrine\ORM\EntityManager;
use Exception;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\HttpFoundation\Request;
use My\CamsBundle\Entity\Application;
use My\CamsBundle\Form\ApplicationType;

/**
 * Apply for a casual position.
 *
 * Class ApplicationController
 * @package My\CamsBundle\Controller
 */
class ApplicationController extends Controller
{

    /**
     * Application form.
     *
     * @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
     *
     * @Route("/application/create")
     */
    public function createAction()
    {
        $application = new Application();
        $em = $this->getDoctrine()->getManager();
        $form = $this->createFormModel($application);

        return $this->render('MyCamsBundle:Application:create.html.twig', array(
            'form' => $form->createView(),
        ));
    }

    /**
     * Application save form.
     *
     * @param \Symfony\Component\HttpFoundation\Request $request
     * @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
     *
     * @Route("/application/save")
     */
    public function saveAction(Request $request)
    {
        $em = $this->getDoctrine()->getManager();
        $raw_application = $request->get('application');
        $request->request->set('application', $raw_application);
        $application = new Application();
        $form = $this->createFormModel($application);
        $form->handleRequest($request);
        $application_id = 0;

        try {
            if ($form->isValid()) {
                $application = $form->getData();

                $em->merge($application);
                $em->flush();
                $application_id = $application->getId();
            } else {
                return $this->render('MyCamsBundle:Application:create.html.twig', array(
                    'form' => $form->createView(),
                ));
            }
        } catch (Exception $e) {
            $request->getSession()->getFlashBag()->add(
                'notice', 'Failed to save changes'
            );
            $logger = $this->get('logger');
            $logger->error($e->getMessage());

            return $this->render('MyCamsBundle:Application:create.html.twig', array(
                'form' => $form->createView(),
            ));
        }

        return $this->redirect($this->generateUrl('my_cams_application_update'));
    }

    /**
     * Application edit form. This is the applicant's dashboard.
     *
     * @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
     *
     * @Route("/application/update")
     */
    public function updateAction()
    {
        $em = $this->getDoctrine()->getManager();

        // etc.
    }

    /**
     * @param $application
     * @return \Symfony\Component\Form\Form
     */
    private function createFormModel($application)
    {
        $form = $this->createForm(new ApplicationType(), $application, array(
                'action' => $this->generateUrl('my_cams_application_save')
            )
        );

        return $form;
    }

}

И репозиторий:

<?php

namespace My\CamsBundle\Entity\Repository;

use Doctrine\ORM\EntityRepository;

/**
 * ApplicationRepository
 *
 * This class was generated by the Doctrine ORM. Add your own custom
 * repository methods below.
 */
class ApplicationRepository extends EntityRepository
{
}

Я не уверен, правильно ли загружается сопоставление для vich_uploader. Я не вижу ничего недостающего в определениях Entity и form.


  • Добро пожаловать в Stack Overflow! Это выглядит как больше кода, чем необходимо. Можете ли вы сократить код до тех пор, пока код нельзя будет удалить, не теряя при этом проблемы? (Кроме того, уценка фрагмента кода совершенно неверна; оставьте их только как блоки кода.) 15.02.2015
  • Какую версию vich/uploader-bundle вы используете? Мне не удалось использовать последнюю версию, и мне пришлось использовать v0.14.0. Этот отлично работает по сравнению с 1.0.*@dev. Может быть, это поможет. Добавьте этот "vich/uploader-bundle": "0.14.0" в свой composer.json. 15.03.2015
  • Версия 1.0.x-dev VichUploader. Перешли на VlabsMediaBundle. 18.06.2015

Ответы:


1

Не совсем уверен, поможет ли это, но измените: $cvUpdatedAt на $updatedAt. В моем случае это сработало.

05.06.2020
Новые материалы

Как свинг-трейдеры могут использовать ИИ для больших выигрышей
По мере того как все больше и больше профессиональных трейдеров и активных розничных трейдеров узнают о возможностях, которые предоставляет искусственный интеллект и машинное обучение для улучшения..

Как построить любой стол
Я разработчик программного обеспечения. Я люблю делать вещи и всегда любил. Для меня программирование всегда было способом создавать вещи, используя только компьютер и мое воображение...

Обзор: Машинное обучение: классификация
Только что закончил третий курс курса 4 часть специализации по машинному обучению . Как и второй курс, он был посвящен низкоуровневой работе алгоритмов машинного обучения. Что касается..

Разработка расширений Qlik Sense с qExt
Использование современных инструментов веб-разработки для разработки крутых расширений Вы когда-нибудь хотели кнопку для установки переменной в приложении Qlik Sense? Когда-нибудь просили..

React Hooks: основы деструктуризации массива
Kent C. Dodds написал классный пост о том, как грядущая функция React под названием Hooks работает на капоте. Предстоящий хук React useState основан на деструктурировании массива, давайте..

Пакеты R, используемые в Tesla
Добро пожаловать обратно! R — очень популярный язык программирования, используемый множеством компаний, включая Tesla! Итак, давайте взглянем на некоторые пакеты R, которые использует Tesla...

Сокращение и слияние токенов для эффективных моделей VL: обзор
Часто в задачах, связанных с компьютерным зрением и НЛП, вычислительно затратная и требующая большого объема памяти обработка становится препятствием для более быстрого логического вывода модели, а..