Uploading images

You can find a base value object that you can use to upload images.

It can be used in combination with the form type Common\Form\ImageType

While most of the things you need to do are already written for you, you will still need to add some configuration for each implementation.

Basic implementation

Create a value object

Not all images are created equal. The image you want to upload has a specific meaning in your application and therefor your implementation should reflect that.

  • Create a new class
  • Extend the class Common\Doctrine\ValueObject\AbstractImage
  • Implement the getUploadDir() method (for documentation about this see the phpdoc)
  • If you want a fallback image you can overwrite the constant FALLBACK_IMAGE

After implementing this your value object will be transformed into the web path of your file when it is sent to the template.

This way you can just use it like myEntity.myImage or if you want to specify a image directory myEntity.myImage.webPath('200x200')

Example

<?php

namespace Backend\Modules\Users\ValueObject;

use Common\Doctrine\ValueObject\AbstractImage;

final class Avatar extends AbstractImage
{
    // optional, null by default
    const FALLBACK_IMAGE = 'no-avatar.png';

    /**
     * @return string
     */
    protected function getUploadDir()
    {
        return 'user/avatar';
    }
}

Create a DBALType

In order to save the file to the database using doctrine we need a DBALType

  • Create a new class
  • Extend the class Common\Doctrine\Type\AbstractImageType
  • Implement the methods createFromString() and getName()
  • Register your DBALType (doctrine.dbal.types)

Example

<?php

namespace Backend\Modules\Users\DBALType;

use Common\Doctrine\Type\AbstractImageType;
use Backend\Modules\Users\ValueObject\Avatar;

final class AvatarType extends AbstractImageType
{
    /**
     * @param string $fileName
     *
     * @return Avatar
     */
    protected function createFromString($imageName)
    {
        return Avatar::fromString($imageName);
    }

    /**
     * @return string
     */
    public function getName()
    {
        return 'avatar';
    }
}

app/config/config.yml or the config file you load via DependencyInjection in the prepend method

doctrine:
  dbal:
    types:
      user_avatar_type: Backend\Modules\Users\DBALType\AvatarType

Update your entity

Now that we have our DBAL type and our value object we can add it to our entity

  • Add a property to your class for your value object
  • Set the column type to the name your DBAL type is registered on
  • Add the @ORM\HasLifecycleCallbacks annotation to the entity
  • Add the lifecycle callbacks to your entity as described in the phpdoc of the Common\Doctrine\ValueObject\AbstractImage class

Example

<?php

namespace Backend\Modules\Users\Entity;

use Doctrine\ORM\Mapping as ORM;
use Backend\Modules\Users\ValueObject\Avatar;

/**
 * User entity
 *
 * @ORM\Table()
 * @ORM\Entity
 * @ORM\HasLifecycleCallbacks()
 */
class User
{
    /**
     * @var Avatar
     *
     * @ORM\Column(type="user_avatar_type")
     */
    protected $avatar;

    /**
     * @return Avatar
     */
    public function getAvatar()
    {
        return $this->avatar;
    }

    /**
     * @param Avatar $avatar
     * @return self
     */
    public function setAvatar($avatar)
    {
        $this->avatar = $avatar;

        return $this;
    }

    /**
     * @ORM\PreUpdate()
     * @ORM\PrePersist()
     */
    public function prepareToUploadAvatar()
    {
        $this->avatar->prepareToUpload();
    }

    /**
     * @ORM\PostUpdate()
     * @ORM\PostPersist()
     */
    public function uploadAvatar()
    {
        $this->avatar->upload();
    }

    /**
     * @ORM\PostRemove()
     */
    public function removeAvatar()
    {
        $this->avatar->remove();
    }
}

Update your form type

For the last step you need to add your file to your form.

  • Use Common\Form\ImageType as the form type
  • Set the fully qualified class name (FQCN) of your value object in the option image_class (tip: you can use MyImage::class for that)

Example

<?php

namespace Backend\Modules\Users\Form;

use Common\Form\ImageType;
use Backend\Modules\Users\ValueObject\Avatar;
use Symfony\Component\Form\FormBuilderInterface;

class UserType
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('avatar', ImageType::class, ['image_class' => Avatar::class]);
    }
}

Advanced implementation

If you want you can use events on the form that has the image type and set a prefix on the generated filename with the method setNamePrefix on the image value object

Extra configuration options

To make your life even easier, the form FileType has some interesting configuration options on top of the default options that the Symfony FileType already has.

  • show_preview: By default we will show a preview of the current image if there is one. You can disable this using this option.
  • preview_class: You can use this option to add an extra class to the preview image, for example you could add img-circle to make the preview image round
  • show_remove_image: If your image is not required we will automatically add the option for the user to remove the image, You can disable this using this option.
  • remove_image_label: You can use it to change the translation label of the remove image checkbox.
  • help_text_message: You can use it to change the help text below the file. This will by default show the max upload size in a nice message
  • help_text_argument: You can use it to change the argument used in the help text below the file. This will by default contain max upload size in a human readable format
  • accept: You can use this to set the accept attribute on the input field and limit the files that can be selected.
  • required_image_error: You can use it to change the translation label of the error when the image is required but no file was uploaded.
  • preview_image_directory: You can use this to use a different image directory in your preview, by default it will use source