mezcalito/sylius-file-upload-plugin

Mezcalito文件上传插件适用于Sylius。


README

此插件的工作方式几乎与标准Sylius图片上传相同(参见Sylius文档中的如何向实体添加图片?),只不过它接受任何类型的文件。

最初这作为Sylius核心的拉取请求被提出,但由于与Sylius 1.x不向后兼容而被拒绝(这里是初始PR的链接),所以这里作为Sylius插件提供。

安装

首先使用composer要求此包

$ composer require mezcalito/sylius-file-upload-plugin

然后将包添加到您的config/bundles.php文件中

// bundles.php

return [
    // ...
    Mezcalito\SyliusFileUploadPlugin\MezcalitoSyliusFileUploadPlugin::class => ['all' => true], 
];

在您的config/packages/_sylius.yaml文件中,添加以下内容

# config/packages/_sylius.yaml
imports:
    - { resource: "@MezcalitoSyliusFileUploadPlugin/Resources/config/app/config.yml" }

此文件定义了插件使用的gaufrette filesystemadapter,您可以根据需要覆盖。

用法

本节是官方Sylius文档关于如何向实体添加图片?的改编。

扩展实体以包含files字段是一个相当常见的用例。在本指南中,我们将展示如何将文件添加到ShippingMethod实体中。

1. 使用FilesAwareInterface扩展ShippingMethod

为了覆盖SyliusCoreBundle中的ShippingMethod,您必须创建自己的ShippingMethod类,并将其扩展

<?php

declare(strict_types=1);

namespace App\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Mezcalito\SyliusFileUploadPlugin\Model\FilesAwareInterface;
use Mezcalito\SyliusFileUploadPlugin\Model\FilesAwareTrait;
use Sylius\Component\Core\Model\ShippingMethod as BaseShippingMethod;

class ShippingMethod extends BaseShippingMethod implements FilesAwareInterface
{
    use FilesAwareTrait {
        __construct as private initializeFilesCollection;
    }
    
    public function __construct() {
        $this->initializeFilesCollection();
    }
}

这里我们使用了提供的FilesAwareTrait以方便使用。

2. 注册您扩展的ShippingMethod作为资源模型类

使用此类配置,您将注册您的ShippingMethod类以覆盖默认类

# config/packages/sylius_shipping.yaml
sylius_shipping:
    resources:
        shipping_method:
            classes:
                model: App\Entity\ShippingMethod

3. 创建ShippingMethodFile

App\Entity命名空间中放置ShippingMethodFile类,其外观如下

<?php

declare(strict_types=1);

namespace App\Entity;

use Mezcalito\SyliusFileUploadPlugin\Model\File;

class ShippingMethodFile extends File
{
}

4. 添加ShippingMethodFile的映射文件

您的新实体将保存到数据库中,因此它需要一个映射文件,其中您将设置ShippingMethodShippingMethodFileowner

# App/Resources/config/doctrine/ShippingMethodFile.orm.yml
App\Entity\ShippingMethodFile:
    type: entity
    table: app_shipping_method_file
    manyToOne:
        owner:
            targetEntity: App\Entity\ShippingMethod
            inversedBy: files
            joinColumn:
                name: owner_id
                referencedColumnName: id
                nullable: false
                onDelete: CASCADE

5. 修改ShippingMethod的映射文件

新添加的files字段必须添加到映射中,与ShippingMethodFile相关联

# App/Resources/config/doctrine/ShippingMethod.orm.yml
App\Entity\ShippingMethod:
    type: entity
    table: sylius_shipping_method
    oneToMany:
        files:
            targetEntity: App\Entity\ShippingMethodFile
            mappedBy: owner
            orphanRemoval: true
            cascade:
                - all

6. 将ShippingMethodFile注册为资源

ShippingMethodFile类需要注册为Sylius资源

# app/config/config.yml
sylius_resource:
    resources:
        app.shipping_method_file:
            classes:
                model: App\Entity\ShippingMethodFile

7. 创建ShippingMethodFileType

这是ShippingMethodFileType类的样子。将其放在App\Form\Type\目录中。

.. code-block:: php

<?php

declare(strict_types=1);

namespace App\Form\Type;

use Mezcalito\SyliusFileUploadPlugin\Form\Type\FileType;

final class ShippingMethodFileType extends FileType
{
    /**
     * {@inheritdoc}
     */
    public function getBlockPrefix(): string
    {
        return 'app_shipping_method_file';
    }
}

8. 将ShippingMethodFileType注册为服务

在创建表单类型类之后,您需要将其注册为如下所示的form.type服务

# services.yml
services:
    app.form.type.shipping_method_file:
        class: App\Form\Type\ShippingMethodFileType
        tags:
            - { name: form.type }
        arguments: ['%app.model.shipping_method_file.class%']

9. 将ShippingMethodFileType添加到资源表单配置中

此外,新的表单类型需要配置为ShippingMethodFile的资源表单

# app/config/config.yml
sylius_resource:
    resources:
        app.shipping_method_file:
            classes:
                form: App\Form\Type\ShippingMethodFileType

10. 扩展ShippingMethodType以包含文件字段

创建Sylius\Bundle\ShippingBundle\Form\Type\ShippingMethodType的表单扩展类

它需要具有作为CollectionType的文件字段。

<?php

declare(strict_types=1);

namespace App\Form\Extension;

use App\Form\Type\ShippingMethodFileType;
use Sylius\Bundle\ShippingBundle\Form\Type\ShippingMethodType;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\FormBuilderInterface;

final class ShippingMethodTypeExtension extends AbstractTypeExtension
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder->add('files', CollectionType::class, [
            'entry_type' => ShippingMethodFileType::class,
            'allow_add' => true,
            'allow_delete' => true,
            'by_reference' => false,
            'label' => 'sylius.form.shipping_method.files',
        ]);
    }

    /**
     * {@inheritdoc}
     */
    public function getExtendedType(): string
    {
        return ShippingMethodType::class;
    }
}

如果您只需要单个文件上传,这可以通过两个非常简单的步骤来完成。

首先,在上面的表单代码中,将 allow_addallow_delete 设置为 false

其次,在您之前定义的 ShippingMethod 实体的 __construct 方法中添加以下内容

public function __construct()
{
    parent::__construct();
    $this->files = new ArrayCollection();
    $this->addFile(new ShippingMethodFile());
}
# services.yml
services:
    app.form.extension.type.shipping_method:
        class: App\Form\Extension\ShippingMethodTypeExtension
        tags:
            - { name: form.type_extension, extended_type: Sylius\Bundle\ShippingBundle\Form\Type\ShippingMethodType }

11. 声明文件上传监听器服务

为了处理文件上传,您需要将 FilesUploadListener 绑定到 ShippingMethod 实体的事件上

.. code-block:: yaml

# services.yml
services:
    app.listener.files_upload:
        class: Mezcalito\SyliusFileUploadPlugin\EventListener\FilesUploadListener
        autowire: true
        autoconfigure: false
        public: false
        tags:
            - { name: kernel.event_listener, event: sylius.shipping_method.pre_create, method: uploadFiles }
            - { name: kernel.event_listener, event: sylius.shipping_method.pre_update, method: uploadFiles }

12. 在表单视图中渲染文件字段

为了实现这一点,您需要自定义来自 SyliusAdminBundle/views/ShippingMethod/_form.html.twig 文件的表单视图。

将内容复制并粘贴到您自己的 app/Resources/SyliusAdminBundle/views/ShippingMethod/_form.html.twig 文件中,并渲染 {{ form_row(form.files) }} 字段。

{# app/Resources/SyliusAdminBundle/views/ShippingMethod/_form.html.twig #}

{% from '@SyliusAdmin/Macro/translationForm.html.twig' import translationForm %}

{# Add the form theme to preview the file with the theme (there is a shortcut for 'fileProductTheme.html.twig' #}
{# Check https://symfony.com.cn/doc/current/form/form_themes.html for help #}
{% form_theme form with [
    '@SyliusAdmin/Form/imagesTheme.html.twig',
    '@MezcalitoSyliusFileUploadPlugin/Form/theme.html.twig'
] %}


<div class="ui two column stackable grid">
    <div class="column">
        <div class="ui segment">
            {{ form_errors(form) }}
            <div class="three fields">
                {{ form_row(form.code) }}
                {{ form_row(form.zone) }}
                {{ form_row(form.position) }}
            </div>
            {{ form_row(form.enabled) }}
            <h4 class="ui dividing header">{{ 'sylius.ui.availability'|trans }}</h4>
            {{ form_row(form.channels) }}
            <h4 class="ui dividing header">{{ 'sylius.ui.category_requirements'|trans }}</h4>
            {{ form_row(form.category) }}
            {% for categoryRequirementChoiceForm in form.categoryRequirement %}
                {{ form_row(categoryRequirementChoiceForm) }}
            {% endfor %}
            <h4 class="ui dividing header">{{ 'sylius.ui.taxes'|trans }}</h4>
            {{ form_row(form.taxCategory) }}
            <h4 class="ui dividing header">{{ 'sylius.ui.shipping_charges'|trans }}</h4>
            {{ form_row(form.calculator) }}
            {% for name, calculatorConfigurationPrototype in form.vars.prototypes %}
                <div id="{{ form.calculator.vars.id }}_{{ name }}" data-container=".configuration"
                     data-prototype="{{ form_widget(calculatorConfigurationPrototype)|e }}">
                </div>
            {% endfor %}

            {# Here you go! #}
            {{ form_row(form.files) }}

            <div class="ui segment configuration">
                {% if form.configuration is defined %}
                    {% for field in form.configuration %}
                        {{ form_row(field) }}
                    {% endfor %}
                {% endif %}
            </div>
        </div>
    </div>
    <div class="column">
        {{ translationForm(form.translations) }}
    </div>
</div>

13. 验证

到目前为止,您的表单工作得很好,但不要忘记验证。最简单的方法是在 App/Resources/config/validation 文件夹下的验证配置文件中进行。

例如,对于图像,它可能看起来像这样

# src\Resources\config\validation\ShippingMethodFile.yml
App\Entity\ShippingMethodFile:
  properties:
    file:
      - Image:
          groups: [sylius]
          maxHeight: 1000
          maxSize: 10240000
          maxWidth: 1000
          mimeTypes:
            - "image/png"
            - "image/jpg"
            - "image/jpeg"
            - "image/gif"
          mimeTypesMessage: 'This file format is not allowed. Please use PNG, JPG or GIF files.'
          minHeight: 200
          minWidth: 200

或者对于PDF,它可能看起来像这样

# src\Resources\config\validation\ShippingMethodFile.yml
App\Entity\ShippingMethodFile:
  properties:
    file:
      - File:
          groups: [sylius]
          maxSize: 10240000
          mimeTypes:
            - "application/pdf"
            - "application/x-pdf"
          mimeTypesMessage: 'This file format is not allowed. Only PDF files are allowed.'

这为每个文件实体定义了验证约束。

最后,将 ShippingMethod 的验证连接到每个单独的 File Entity 的验证

# src\Resources\config\validation\ShippingMethod.yml
App\Entity\ShippingMethod:
  properties:
    ...
    images:
      - Valid: ~

14. 迁移

执行数据库迁移

$ bin/console doctrine:migrations:diff
$ bin/console doctrine:migrations:migrate