鍍金池/ 教程/ PHP/ 如何創(chuàng)建一個(gè)表單類(lèi)型擴(kuò)展
如何以非繼承方式自定義方法
如何創(chuàng)建事件監(jiān)聽(tīng)器
如何以非繼承方式擴(kuò)展一個(gè)類(lèi)
如何記錄消息到不同的文件
如何掌握并創(chuàng)建新的環(huán)境
如何使用 Doctrine DBAL
"XXX is deprecated" E-USER-DEPRECATED 的警告是什么意思?
在登錄表單中使用 CSRF 保護(hù)
如何注冊(cè)自定義 DQL 函數(shù)
如何為表單類(lèi)配置空數(shù)據(jù)
如何嵌入集合表單
如何創(chuàng)建自定義認(rèn)證提供者
如何使用 Apache Router
如何組織配置文件
部署在 Microsoft Azure 云
如何在路由參數(shù)中允許"/"字符
如何在安全,路由,服務(wù)和驗(yàn)證中使用表達(dá)式
如何對(duì)顯示控制臺(tái)信息配置 Monolog
如何為一個(gè) Bundle 創(chuàng)建友好的配置
如何改變默認(rèn)的目標(biāo)路徑行為
如何在運(yùn)行測(cè)試之前自定義引導(dǎo)過(guò)程
如何從路由向控制器傳輸額外的信息
如何從數(shù)據(jù)庫(kù)(實(shí)體提供者)讀取安全用戶
如何從 Controller 調(diào)用一個(gè)命令
如何創(chuàng)建自定義表單密碼驗(yàn)證器
如何使用內(nèi)建的 PHP Web 服務(wù)器
如何在功能測(cè)試中用 Token 模擬認(rèn)證
配置 Session 文件的保存目錄
理解前端控制器、內(nèi)核及環(huán)境如何協(xié)同工作
如何實(shí)現(xiàn)一個(gè)簡(jiǎn)單的注冊(cè)表單
如何使用 Doctrine 擴(kuò)展:Timestampable, Sluggable, Translatable 等等
如何使用多個(gè)實(shí)體管理器和連接
如何自定義表單渲染
如何安裝第三方 Bundles
使用預(yù)認(rèn)證安全防火墻
如何簡(jiǎn)化多個(gè) Bundle 的配置
會(huì)話代理實(shí)例
安裝 Composer
如何冒充一個(gè)用戶
如何注冊(cè)一個(gè)新的請(qǐng)求格式和 MIME 類(lèi)型
如何在功能測(cè)試中使用分析器
如何在服務(wù)容器內(nèi)設(shè)置外部參數(shù)
如何重寫(xiě) Symfony 默認(rèn)的目錄結(jié)構(gòu)
如何在一個(gè) Symfony 控制器中創(chuàng)建一個(gè) SOAP 的 Web 服務(wù)
如何使用序列化
部署在 Platform.sh
升級(jí)一個(gè)副版本
如何寫(xiě)一個(gè)自定義的 Twig 擴(kuò)展
如何在 SubVersion 中創(chuàng)建并保存一個(gè) Symfony 項(xiàng)目
使用 PHP 庫(kù)聯(lián)合,編譯和最小化 Web 資產(chǎn)
如何創(chuàng)建一個(gè)自定義的數(shù)據(jù)收集器
如何使用和注冊(cè)命名空間路徑
如何使用 Monolog 記錄日志
如何建立一個(gè)傳統(tǒng)的登錄表單
如何強(qiáng)制路由總是使用 HTTPS 或者 HTTP
如何在模板中使用 PHP 而不是 Twig
如何動(dòng)態(tài)選擇密碼加密算法
避免匿名用戶開(kāi)始 Session 會(huì)話
如何測(cè)試 Doctrine 倉(cāng)庫(kù)
如何在功能測(cè)試中測(cè)試一封電子郵件被發(fā)送
Symfony2 與 Symfony1 的區(qū)別
使用結(jié)尾反斜線重定向 URL
(configuration)如何在數(shù)據(jù)庫(kù)中使用 PdoSessionHandler 存儲(chǔ) Sessions
如何使用匹配器有條件地啟用分析器
部署在 Heroku 云
如何不用自定義控制器配置重定向
如何在 Bundle 內(nèi)部加載服務(wù)配置
如何處理不同的錯(cuò)誤級(jí)別
如何在應(yīng)用中保護(hù)服務(wù)和方法
如何對(duì)表單單元測(cè)試
如何把命令定義為服務(wù)
如何配置 Monolog 從日志中排除 404 錯(cuò)誤
如何使用控制臺(tái)
如何測(cè)試與數(shù)據(jù)庫(kù)交互的代碼
如何在路由中使用除了 GET 和 POST 的 HTTP 方法
如何使用云服務(wù)發(fā)送電子郵件
如何創(chuàng)建一個(gè)控制臺(tái)命令
在遺留的應(yīng)用上使用 Symfony Session
如何使用高級(jí)的訪問(wèn)控制列表
如何不用一個(gè)自定義的控制器渲染一個(gè)模板
如何重寫(xiě)部分 Bundle
升級(jí)一個(gè)主版本
安全訪問(wèn)控制是如何工作的
如何使用 Bundle 的繼承來(lái)重寫(xiě)部分 Bundle
如何使用 Voter 檢查用戶權(quán)限
如何為多個(gè) Doctrine 的實(shí)現(xiàn)提供模型類(lèi)
如何使用作用域
如何部署一個(gè) Symfony 應(yīng)用
如何用 "inherit-data" 減少代碼冗余
如何注冊(cè)事件監(jiān)聽(tīng)器和訂閱
使用 Bower 安裝 Symfony
如何創(chuàng)建一個(gè)自定義路由加載器
如何創(chuàng)建一個(gè)自定義的驗(yàn)證限制
在獨(dú)立注入類(lèi)中使用參數(shù)
如何使用 Assetic 和 Twig Functions 進(jìn)行圖像優(yōu)化
如何利用表單事件動(dòng)態(tài)修改表單
如何在過(guò)濾器的前后設(shè)置事件分發(fā)器
如何在 Bundle 中使用 Compiler Passes
緩存包含 CSRF 保護(hù)表單的頁(yè)面
如何注入變量到所有的模板(如全局變量)
如何創(chuàng)建一個(gè)自定義表單域類(lèi)型
如何限定防火墻使其只允許通過(guò)指定請(qǐng)求
如何把 Controller 定義為服務(wù)
如何使用 Gmail 發(fā)送郵件
升級(jí)一個(gè)補(bǔ)丁版本
如何創(chuàng)建一個(gè)表單類(lèi)型擴(kuò)展
如何對(duì)不同的 URL 強(qiáng)制使用 HTTPS 或者 HTTP
如何使用 Varnish 加速我的 Web 站點(diǎn)
如何定義虛擬類(lèi)和接口之間的關(guān)系
如何自定義登錄表單
如何測(cè)試多個(gè)客戶端的交互
PSR-7 Bridge
如何使用 YUI Compressor 裁剪 Javascripts 和 Stylesheets
在用戶的 Session 中使用局部 "Sticky"
如何定制錯(cuò)誤頁(yè)
如何從已存在的數(shù)據(jù)庫(kù)中生成實(shí)體
如何用 Doctrine 上傳文件
可復(fù)用 Bundles 的最佳實(shí)踐
如何發(fā)送一封電子郵件
如何將 Assetic Filter 應(yīng)用到具體的文件擴(kuò)展名上
切換分析器存儲(chǔ)
如何上傳文件
限制 Session 元數(shù)據(jù)的寫(xiě)入
如何使用多用戶提供者
如何使用數(shù)據(jù)轉(zhuǎn)換
配置一個(gè) Web 服務(wù)器
如何編程訪問(wèn)分析器數(shù)據(jù)
如何在路由中使用服務(wù)容器參數(shù)
如何在控制臺(tái)生成 URL 和發(fā)送郵件
如何將你的開(kāi)發(fā)環(huán)境優(yōu)化為調(diào)試環(huán)境
如何在控制臺(tái)命令中啟用日志
如何在功能測(cè)試中模擬 HTTP 認(rèn)證
如何使用 API 驗(yàn)證用戶
如何移除 AcmeDemoBundle
控制臺(tái)命令
如何配置 Symfony 使其工作在負(fù)載均衡和反轉(zhuǎn)代理
如何添加“記住我”登錄功能
如何使用 Assetic 進(jìn)行資產(chǎn)管理
如何限定防火墻使其接受指定主機(jī)
如何使用訪問(wèn)控制列表(ACLs)
如何裁剪 CSS/JS 文件(使用 UglifyJS 和 UglifyCSS)
如何緩存電子郵件
如何使用 submit() 函數(shù)處理表單提交
如何在 Git 中創(chuàng)建并保存一個(gè) Symfony 項(xiàng)目
如何創(chuàng)建自定義用戶提供者
如何對(duì)電子郵件錯(cuò)誤配置 Monolog
如何在開(kāi)發(fā)時(shí)使用電子郵件

如何創(chuàng)建一個(gè)表單類(lèi)型擴(kuò)展

當(dāng)你需要特定目的的表單類(lèi)型時(shí)自定義表單字段類(lèi)型就很好,例如性別選擇或者增值稅發(fā)票號(hào)碼輸入。

但是有時(shí)候,你不需要添加新的字段類(lèi)型——你只是想要在已經(jīng)存在的上面添加一些特征。這時(shí)候表單類(lèi)型擴(kuò)展就出現(xiàn)了。

表單類(lèi)型擴(kuò)展主要有兩種用途:

  1. 你想要向幾種類(lèi)型中添加一般特征(例如向每一個(gè)字段類(lèi)型添加“幫助”文本);
  2. 你想要向一種類(lèi)型中添加特定特征(例如向“文件”字段添加“下載”特征)。

在這兩種情況下,通過(guò)定制的表單渲染或者定制的表單字段類(lèi)型可能會(huì)實(shí)現(xiàn)你的目標(biāo)。但是使用表單類(lèi)型擴(kuò)展可以更加清楚(通過(guò)限制模板中的業(yè)務(wù)邏輯)并且更靈活(你可以向一個(gè)表單類(lèi)型中添加幾個(gè)類(lèi)型擴(kuò)展)。

表單類(lèi)型擴(kuò)展能夠完成大多是定制的字段類(lèi)型能做的事情,但是代替自己成為字段類(lèi)型,它們插入到已經(jīng)存在的類(lèi)型中。

假設(shè)你管理一個(gè)媒體實(shí)體,并且每一個(gè)媒體都和一個(gè)文件相關(guān)聯(lián)。你的媒體表單使用了文件類(lèi)型,但是當(dāng)編輯這個(gè)實(shí)體的時(shí)候,你就會(huì)看到它的臨近文件輸入的圖像自動(dòng)渲染。

你當(dāng)然可以通過(guò)在模板中配置字段如何被渲染。但是字段類(lèi)型擴(kuò)展允許你以一種更加流行的方式。

定義表單類(lèi)型擴(kuò)展

你的第一個(gè)任務(wù)就是創(chuàng)建一個(gè)表單類(lèi)型擴(kuò)展類(lèi)(在本文中叫做 ImageTypeExtension)。標(biāo)準(zhǔn)情況下表單類(lèi)型擴(kuò)展通常位于你的某一個(gè) bundle 的 Form\Extension 目錄之下。

當(dāng)創(chuàng)建表單類(lèi)型擴(kuò)展的時(shí)候,你可以啟用 FormTypeExtensionInterface 界面或者擴(kuò)展 AbstractTypeExtension 類(lèi)。大多數(shù)情況下擴(kuò)展 abstract 類(lèi)更容易一些:

// src/Acme/DemoBundle/Form/Extension/ImageTypeExtension.php
namespace Acme\DemoBundle\Form\Extension;

use Symfony\Component\Form\AbstractTypeExtension;

class ImageTypeExtension extends AbstractTypeExtension
{
    /**
     * Returns the name of the type being extended.
     *
     * @return string The name of the type being extended
     */
    public function getExtendedType()
    {
        return 'file';
    }
}

你必須使用的一個(gè)方法就是 getExtendedType 功能。它是用來(lái)被你的擴(kuò)展所擴(kuò)展的表單類(lèi)型的名稱(chēng)的。

getExtendedType 方法返回的值和你希望擴(kuò)展的表單類(lèi)型類(lèi)的 getName 方法所返回的值相一致。

處理 getExtendedType 方法,你可能還想重寫(xiě)下列的一個(gè)方法:

  • buildForm()
  • buildView()
  • configureOptions()
  • finishView()

有關(guān)于這些方法的用途的更多信息,你可以參考創(chuàng)建自定義表單類(lèi)型這篇指導(dǎo)文章。

將你的表單類(lèi)型擴(kuò)展注冊(cè)為服務(wù)

接下來(lái)這一步就是使得 Symfony 知道你的擴(kuò)展。你所需要做的就是使用 form.type_extension 標(biāo)簽將它聲明為一個(gè)服務(wù):

YAML:

services:
    acme_demo_bundle.image_type_extension:
        class: Acme\DemoBundle\Form\Extension\ImageTypeExtension
        tags:
            - { name: form.type_extension, alias: file }

XML:

<service id="acme_demo_bundle.image_type_extension"
    class="Acme\DemoBundle\Form\Extension\ImageTypeExtension"
>
    <tag name="form.type_extension" alias="file" />
</service>

PHP:

$container
    ->register(
        'acme_demo_bundle.image_type_extension',
        'Acme\DemoBundle\Form\Extension\ImageTypeExtension'
    )
    ->addTag('form.type_extension', array('alias' => 'file'));

標(biāo)簽的別名值就是擴(kuò)展將要應(yīng)用到的字段的類(lèi)型。在你應(yīng)用的時(shí)候,如果你想要擴(kuò)展文件字段類(lèi)型,你就可以用文件作為別名。

給擴(kuò)展添加業(yè)務(wù)邏輯

你的擴(kuò)展的目標(biāo)就是展示完美的圖片在文件輸入旁邊(當(dāng)基本類(lèi)型包括圖片的時(shí)候)。為了達(dá)到這個(gè)目的,假設(shè)你是用的方法和如何使用 Doctrine 處理文件上傳中所描述的一樣:你有一個(gè)具有文件屬性的媒體模型(和表單中的文件字段相對(duì)應(yīng))以及一個(gè)路徑屬性(和數(shù)據(jù)庫(kù)中的圖片路徑相對(duì)應(yīng)):

// src/Acme/DemoBundle/Entity/Media.php
namespace Acme\DemoBundle\Entity;

use Symfony\Component\Validator\Constraints as Assert;

class Media
{
    // ...

    /**
     * @var string The path - typically stored in the database
     */
    private $path;

    /**
     * @var \Symfony\Component\HttpFoundation\File\UploadedFile
     * @Assert\File(maxSize="2M")
     */
    public $file;

    // ...

    /**
     * Get the image URL
     *
     * @return null|string
     */
    public function getWebPath()
    {
        // ... $webPath being the full image URL, to be used in templates

        return $webPath;
    }
}

你的表單類(lèi)型擴(kuò)展類(lèi)將需要做兩件事來(lái)擴(kuò)展文件表單類(lèi)型:

  1. 重寫(xiě) configureOptions 方法來(lái)添加 image_path 選項(xiàng);
  2. 重寫(xiě) buildFormbuildView 方法來(lái)將圖片的地址傳遞到視圖。

邏輯如下:當(dāng)添加文件類(lèi)型的表單字段,你就能制定一個(gè)新的選項(xiàng):image_path。這個(gè)選項(xiàng)將會(huì)告訴文件字段如何獲得實(shí)際的圖片地址并且在視圖中展示它:

// src/Acme/DemoBundle/Form/Extension/ImageTypeExtension.php
namespace Acme\DemoBundle\Form\Extension;

use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\OptionsResolver\OptionsResolver;

class ImageTypeExtension extends AbstractTypeExtension
{
    /**
     * Returns the name of the type being extended.
     *
     * @return string The name of the type being extended
     */
    public function getExtendedType()
    {
        return 'file';
    }

    /**
     * Add the image_path option
     *
     * @param OptionsResolver $resolver
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefined(array('image_path'));
    }

    /**
     * Pass the image URL to the view
     *
     * @param FormView $view
     * @param FormInterface $form
     * @param array $options
     */
    public function buildView(FormView $view, FormInterface $form, array $options)
    {
        if (array_key_exists('image_path', $options)) {
            $parentData = $form->getParent()->getData();

            if (null !== $parentData) {
                $accessor = PropertyAccess::createPropertyAccessor();
                $imageUrl = $accessor->getValue($parentData, $options['image_path']);
            } else {
                 $imageUrl = null;
            }

            // set an "image_url" variable that will be available when rendering this field
            $view->vars['image_url'] = $imageUrl;
        }
    }

}

重寫(xiě)文件控件模板碎片

每一個(gè)字段類(lèi)型都是由模板碎片所渲染的。那些模板碎片可以被重寫(xiě)從而來(lái)自定義表單渲染。獲取更多信息,你可以閱讀什么是表單主題?這篇文章。

在你的擴(kuò)展類(lèi)之中,你已經(jīng)添加了一個(gè)新的變量(image_url),但是你依舊需要使用你的模板中的新的變量。特別的,你需要重寫(xiě) file_widget 區(qū)域:

Twig:

{# src/Acme/DemoBundle/Resources/views/Form/fields.html.twig #}
{% extends 'form_div_layout.html.twig' %}

{% block file_widget %}
    {% spaceless %}

    {{ block('form_widget') }}
    {% if image_url is not null %}
        <img src="{{ asset(image_url) }}"/>
    {% endif %}

    {% endspaceless %}
{% endblock %}

PHP:

<!-- src/Acme/DemoBundle/Resources/views/Form/file_widget.html.php -->
<?php echo $view['form']->widget($form) ?>
<?php if (null !== $image_url): ?>
    <img src="<?php echo $view['assets']->getUrl($image_url) ?>"/>
<?php endif ?>

你需要改變你的配置文件或者明確指定你想要如何給你的表單加主題為了使 Symfony 使用你所重寫(xiě)的區(qū)域。更多信息詳見(jiàn)什么是表單主題?這篇文章。

使用表單類(lèi)型擴(kuò)展

從現(xiàn)在起,當(dāng)在你的表單中添加文件類(lèi)型的字段時(shí),你就可以指定 image_path 選項(xiàng),這個(gè)選項(xiàng)將用來(lái)展示文件字段旁的圖片。舉例來(lái)說(shuō):

// src/Acme/DemoBundle/Form/Type/MediaType.php
namespace Acme\DemoBundle\Form\Type;

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

class MediaType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('name', 'text')
            ->add('file', 'file', array('image_path' => 'webPath'));
    }

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

當(dāng)展示表單的時(shí)候,如果基本的模型已經(jīng)和圖片關(guān)聯(lián),你就會(huì)看到它在文件輸入旁邊顯示。