2019年3月25日 星期一

Symfony4.2入門教學【第六篇】設定(Configuration)


Symfony可以安裝第三方套件(程序包、libraries等等),為專案帶來新的功能(service),透過預設放在目錄config/的設定檔案可以客製每一個套件。

Configuration: config/packages/

所有套件的設定都可以在config/packages/找到。舉例來說,framework程序包的設定檔案在config/packages/framework.yaml
// 省略程式碼
最上層的鍵(key)(這邊指的是framework)會是針對特定程序包的設定(這個例子裡指的是FrameworkBundle)。

設定檔格式
這份文章裡所有的設定檔例子都會有三種格式(YAML, XML and PHP),預設會使用YAML,但你可以選擇你最喜歡的來用,它們的效能表現沒有差別:
  • YAML格式: 簡單、整潔且可讀性高。
  • XML: 相較於YAML,在times和IDE autocompletion的支援度較高。
  • PHP: 非常強大,但相較於標準的設定格式,可讀性較低。

配置參考(Configuration Reference)和Dumping

有兩個方法可以知道有哪些鍵(key)可以設置:
  1. 查看『參考』(原文)這個章節
  2. 使用指令config:dump-reference
舉例來說,如何想要設定關於framework程序包,透過下列指令,你可以看到所有可用的選項羅列出來:
php bin/console config:dump-reference framework


The parameters Key: Parameters (Variables)

配置文件中最特別的最上層鍵,一種被稱為parameters,用來定義可以被其他設定檔引用的變數(variables),例如,當你安裝一個translation套件,一個locale的parameter就會被加在檔案config/services.yaml內。
// 省略程式碼
這個parameter在framework的設定檔(config/packages/translation.yaml)裡可以參考(reference):
// 省略程式碼
在設定檔內,你可以在parameters鍵底下定義任何想要的參數名稱(parameter name),若要參考parameter,在名稱的兩個加上百分比的符號 - 如:%locale%

你可以動態的設定參數,像是環境變數,請看『How to Set external Parameters in the Service Container

想要知道更多關於參數的資訊 - 包含如何在控制器裡參考這些參數 - 請看服務參數(原文)。

檔案.env和環境變數
載入檔案.env的內容成為環境變數(environment variables),在開發階段的時候,或設定環境變數對你來說很困難,都很方便。

當你安裝套件時,更多的環境變數會加到這個檔案,當然你還是可以新增自己所需要的。

透過特定的語句,在其他設定檔都可以引用環境變數。舉例來說,如果你安裝doctrine套件,檔案.env裡會多一個環境變數叫DATABASE_URL,在檔案config/packages/doctrine.yaml裡可以這樣引用參數:
# config/packages/doctrine.yaml
doctrine:
    dbal:
        url: '%env(DATABASE_URL)%'

        # The `resolve:` prefix replaces container params by their values inside the env variable:
        # url: '%env(resolve:DATABASE_URL)%'
若要取得更多關於環境變數的訊息,請看『環境變數』(原文)。

在2018年11月以前建立的專案,系統作了些微的調整,涉及檔案.env.dist,若要了解關於升級的資訊,請看『Nov 2018 Changes to .env & How to Update

檔案.env很特殊,因為它通常定義了在每個伺服器上需要更改的值,例如你本地端的開發環境,資料庫設定肯定和你的同事不同,檔案.env內包含所有環境變數之敏感、無法隱藏的默認值,且應該提交到你的儲存庫(Repository)。

如果要複寫這些根據伺服器或屬於敏感資料的環境變數,可以新建一個檔案.env.local,這將不會上傳到共享的儲存庫。事實上,Symfony自動產生的檔案.gitignore,避免將這些檔案上傳。

你也可以新增一些其他.env,一樣會被載入使用:
  • .env.{environment}: 例如.env.test將會在環境test載入使用,並上傳。
  • .env.{environment}.local: 例如.env.prod.local會在prod環境內載入,但不會上傳到儲存庫。
如果你打算在正式環境中設定正確的環境變數,.env仍然會載入,但你本地端的環境變數會覆蓋掉這些值。


環境&其他設定檔

你有一個應用程式,但不管你有沒有發覺,你需要讓他在不同的時候有不同的表現:
  • 開發(developing)時,你會希望應用程式會紀錄所有內容並顯示在漂亮的除錯工具上。
  • 在部屬到正式(production)環境後,你會希望應用程式在速度方便達到最佳化,且只需要顯示錯誤訊息。
如何讓一個應用程式有兩種不同的表現呢?正確答案是環境(environment)

你可能根本不知道dev環境是什麼就已經在使用了,在你部屬之後,你將會使用prod這個環境。

若要了解更多關於執行和控制每一個環境,請看『如何掌握和建立新環境』(原文)。


繼續前進!

恭喜!你已經了解基礎的Symfony,接著,按照指南了解Symfony的每個部分。查看:

還有更多主題

閱讀更多



文章原文:
https://symfony.com/doc/4.2/configuration.html

系列文章:

2019年3月24日 星期日

Symfony4.2入門教學【第五篇】模板(Templates)

如同上一篇文章的說明,控制器負責處理對Symfony應用的每一個請求,產生回應的內容通常是呈現模板(rendering a template)。

實際上,控制器將大部分繁重的工作委託給第三方,以便測試或重複利用程式碼。當控制器需要產生HTML、CSS或其他內容,這個工作會交給模板引擎(template engine)。

在這篇文章裡,您將學習如何編寫可用於將內容回傳給用戶,填寫電子郵件正文等的強大模板。你將會學到捷徑、巧妙地延伸模板的方法和如何重複利用模板程式碼。


模板

模板是一種能生成任何基本文字格式的文字檔(HTML, CSS, CSV, LaTex...),你應該很熟悉PHP模板 - 能讓PHP解讀的一種摻雜文字和PHP程式碼的文字檔。
<!DOCTYPE html>
<html>
    <head>
        <title>Welcome to Symfony!</title>
    </head>
    <body>
        <h1><?= $page_title ?></h1>

        <ul id="navigation">
            <?php foreach ($navigation as $item): ?>
                <li>
                    <a href="<?= $item->getHref() ?>">
                        <?= $item->getCaption() ?>
                    </a>
                </li>
            <?php endforeach ?>
        </ul>
    </body>
</html>
但Symfony提供更強大的模板語言稱Twig,Twig能使你寫出更簡潔易讀的模板,對web設計人員更友善,並在某些方面,比PHP模板更加強大:
<!DOCTYPE html>
<html>
    <head>
        <title>Welcome to Symfony!</title>
    </head>
    <body>
        <h1>{{ page_title }}</h1>

        <ul id="navigation">
            {% for item in navigation %}
                <li><a href="{{ item.href }}">{{ item.caption }}</a></li>
            {% endfor %}
        </ul>
    </body>
</html>
Twig訂了三種特殊語句:
{{ ... }}
"Say something": 在模板上印出變數或表示式的結果。

{% ... %}
"Does something": 一個能控制模板邏輯的標籤(tag),可以用來執行陳述(statement),像是迴圈(for-loop)。

{# ... #)
"Comment something": 功能等同與PHP的句法/* comment */,用來新增多行或單行的註解,這些內容不會顯示在呈現的頁面上。

Twig也包含過濾器(filters),能在呈現頁面之前改變內容,下面的例子是讓title這個變數在呈現之前全部變成大寫:
{{ title|upper }}
預設情況下,這長串的tagsfiltersfunctions都可以在Twig使用,透過Twig Extension,你也可以克制自己的過濾器(filters)、函式(functions)等等,執行下面的指令看看有哪些:
php bin/console debug:twig
Twig程式碼看起來和PHP程式很像,只有一些微妙但美好的差異,下面的例子是用標準的標籤for和函式cycle()印出奇數、偶數交替使用不同class的10個div標籤:

在本文中,Twig和PHP模板範例都會有。

選擇Twig的原因?
Twig模板很簡單,並忽略不處理PHP標記,Twig模板系統設計的目的是用於表示式,而非程序邏輯。長期接觸後會慢慢愛上它並從中受益,當然也會都到各地web設計人員的青睞。

Twig還能達成PHP不能辦到的事情,像是控制空白、沙盒(sandboxing)、HTML自動轉譯(automatic HTML escaping)、手動內容輸出轉譯(manual contextual output escaping),和僅用於模板的自定義函數和過濾器。Twig包含許多功能,使得撰寫模板更加容易、簡潔,請看下面的範例,結合了環圈和邏輯陳述句if
<ul>
    {% for user in users if user.active %}
        <li>{{ user.username }}</li>
    {% else %}
        <li>No users found</li>
    {% endfor %}
</ul>

Twig模板快取
由於每個模板編譯成一個本地的PHP類(class)並進行緩存,使得Twig運行速度很快,而且這些事情都會自動執行,你什麼都不用做也不用你操心。開發時,Twig會聰明地在你做任何改變之後重新編譯,這代表Twig在正式環境中很快,但開發同時也很方便使用。


模板繼承與佈局

專案的模板通盛會共享一些共通元素,像是header、footer、sidebar等等,在Symfony裡比虛幻一個方式思考:模板可以讓另一個模板裝飾(decorate),這個概念相當於PHP類中的繼承,模板繼承允許你建立一個基本的佈局模板,其中包含定義為區塊(block)的常見元素(可以當作是有一些基本方法的PHP類),子模板能夠延伸基礎佈局,並覆寫父模板的任何區塊(可以想成PHP的子類也會覆蓋父類的某些方法)。

首先,建立一個基處佈局(base layout)的檔案:
{# templates/base.html.twig #}
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>{% block title %}Test Application{% endblock %}</title>
    </head>
    <body>
        <div id="sidebar">
            {% block sidebar %}
                <ul>
                    <li><a href="/">Home</a></li>
                    <li><a href="/blog">Blog</a></li>
                </ul>
            {% endblock %}
        </div>

        <div id="content">
            {% block body %}{% endblock %}
        </div>
    </body>
</html>

雖然關於模板繼承的討論將以Twig的形式進行,但Twig和PHP模板之間的理念是相同的。

這個模板定義基礎的HTML架構(skeleton)是一個兩大欄的頁面,在這個例子裡,定義了三個{% block %}區塊(titlesidebarbody),這個模板也可以直接被渲染,這麼做的話,titlesidebarbody三個區塊會在模板裡保持預設值。

子模板(child template)看起來可能會像是:
{# templates/blog/index.html.twig #}
{% extends 'base.html.twig' %}

{% block title %}My cool blog posts{% endblock %}

{% block body %}
    {% for entry in blog_entries %}
        <h2>{{ entry.title }}</h2>
        <p>{{ entry.body }}</p>
    {% endfor %}
{% endblock %}

父模板(parent template)會存在目錄template/裡,所以路徑為base.thml.twig,模板命名慣例在『模板命名與路徑』(原文)有更完整的說明。

模板繼承的關鍵字為這個{% extends %}標籤,這會告訴模板引擎先解析這個設定好佈局和定義一些區塊的基礎模板,子模板會根據指向titlebody的區塊取代原本父模板的內容進行呈現,根據blog_entries的值,輸出結果如下所示:
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>My cool blog posts</title>
    </head>
    <body>
        <div id="sidebar">
            <ul>
                <li><a href="/">Home</a></li>
                <li><a href="/blog">Blog</a></li>
            </ul>
        </div>

        <div id="content">
            <h2>My first post</h2>
            <p>The body of the first post.</p>

            <h2>Another post</h2>
            <p>The body of the second post.</p>
        </div>
    </body>
</html>
注意一下,既然子模板沒有定義sidebar這個區塊,內容就會沿用父模板的設定,在{% block %}裡的內容總是當作預設值來使用。

想要的話,你可以使用多層的模板繼承,請看『如何用繼承模板管理Twig模板』(原文)了解更多資訊。

使用模板繼承時,有些小技巧請銘記在心:
  • 如果在模板中使用{% extends %},這個標籤必須放在模板的最前面。
  • 在基礎模板有越多{% block %}的標籤越好,記住!子模板不需要定義父模板內所有的區塊,所以你可以根據需求盡可能地在基礎模板中建立越多的區塊,並幫每個區塊定義合理的預設值,基礎模板有越多區塊,能使你的佈局更加彈性。
  • 假設你發現在多個模板內有重複的內容,這可能代表你需要把這部分的內容移到父模板的{% block %}內當作一個區塊。在某些情況下,有一個更好的解法是把這些內容放到新的模板,並引用(include)(請看Including other Templates)。
  • 如果你需要取得父模板區塊的內容,你可以用函式{{ parent() }},你如果想要增加父模板區塊的內容而不是整個覆寫它的話,這會是一個很好用的方法。
{% block sidebar %}
    <h3>Table of Contents</h3>

    {# ... #}

    {{ parent() }}
{% endblock %}



模板命名與路徑

預設情況下,模板會放在兩個不同的路徑:
templates/
應用專案中的目錄views有應用程序範圍的基本模板(例如你應用中的布局、應用程序包的模板)。

vendor/path/to/CoolBundle/Resources/views/
每一個第三方程序在目錄Resources/views/(和子目錄)有它的模板,當你決定分享你的程序包時,你應該將模板放到程序包而非templates/這個目錄。

大部分你使用的模板會放在目錄templates/裡,你的使用路徑是依據這個目錄的相對路徑,舉例來說,如果要render/extendtemplates/base.html.twig,你會用路徑base.html.twig;如果要render/extend檔案templates/blog/index.html.twig,你會用路徑blog/index.html.twig

引用程序包裡的模板
假設你需要引用程序包裡的模板,Symfony使用Twig命名空間(namespace)的語句(@BundleName/directory/filename.html.twig),這能允許使用多種模板,而每一個都在它特定的路徑:
  • @AcmeBlog/Blog/index.html.twig: 這個語句用來指定指定特定頁面的模板,這三部分的字串分別用斜線(/)分隔開來,代表:
    • Blog: 這是程序包的名稱去掉後綴的Bundle,這個模板在AcmdBlogBundle(例如:src/Acme/BlogBundle)
    • index.html.twig: 目錄,表示模板位於Resources/views/的子目錄中。
    • : 檔案名稱,檔案的名稱為index.html.twig
  • @AcmeBlog/layout.html.twig: 這個句法引用屬於AcmeBlogBundle的基本模板,因為少了中間"目錄"的部分(如Blog),這個模板位於AcmeBlogBundle裡的Resources/views/layout.html.twig

使用這個命名空間的語句,而非真正的檔案路徑,來覆寫任何程序包內的模板。

模板後綴(Template Suffix)
每一個模板名稱都有兩個擴展(extension),指定該模板的格式(format)引擎(engine)

檔案名稱格式引擎
blog/index.html.twigHTMLTwig
blog/index.html.phpHTMLPHP
blog/index.css.twigCSSTwig

預設情況下,任何Symfony模板可以用Twig或PHP寫,最後一部分的擴展(e.g. .twig.php)指定這兩種引擎該用哪一個,第一部分的擴展(e.g. ..html.css等等)是模板最終產生的格式,這跟引擎不一樣,它決定了Symfony要如何解讀這個模板,這是有一種策略,用來處理將相同資源呈現成HTML()、XML或任何其他格式,更多的資訊可以讀『如何運用不同輸出格式的模板』(原文)這個章節。


標籤和小幫手

你已經了解基本的模板,如何命名、如何繼承模板,你已經度過最困難的部分,在這個章節,你將會學習很多能執行最常見的模板任務的工具,像是引用其他模板、連結到其他頁面或引用影像。

Symfony帶有幾個專門的Twig標籤和函式,可以簡化模板設計師的工作。在PHP中,模板系統提供一個可擴展的輔助系統,可以在模板的內容(context)中提供有用的功能。

你已經看過幾個內建的Twig標籤,像是{% block %}{% extends %},接著你會學到更多。

引用其他模板
你將會常常在一些頁面上想要引用相同的模板或部分的程式,例如,一個應用中的"最新文章",這個顯示文章用的模板程式,可能可以用於顯示文章詳細訊息的頁面上、顯示最受歡迎文章的頁面上,或在最新文章列表中。

當你需要重複使用一大堆PHP程式,一般來說,你會將這些程式碼搬到一個新的PHP類(class)或函式,這個同樣適用於模板,將重複使用的模板程式碼建立成自己獨立的模板,可以讓任何其他模板引用。首先,建立一個要重複使用的模板:
{# templates/article/article_details.html.twig #}
<h2>{{ article.title }}</h2>
<h3 class="byline">by {{ article.authorName }}</h3>

<p>
    {{ article.body }}
</p>
請用函式{{ include() }}來達成讓其他模板引用這個模板。
{# templates/article/list.html.twig #}
{% extends 'layout.html.twig' %}

{% block body %}
    <h1>Recent Articles<h1>

    {% for article in articles %}
        {{ include('article/article_details.html.twig', { 'article': article }) }}
    {% endfor %}
{% endblock %}
請注意,模板名稱遵循相同的典型約定,article_details.html.twig這個模板使用我們傳過去的變數article,在這個情況下,你可以不需要這樣做,因為所有list.html.twig中可用的變數,一樣可以在article_details.html.twig中使用(除非你將with_context設定false)。

句法{'article': article}是一個標準的Twig語句(i.e. 一個帶有命名鍵的數組),如果你需要傳多個元數,會像是{'foo': foo, 'bar': bar}

連結其他頁面
在應用哩,建立一個連結到其他頁面對模板來說,是一個相當常見的功能,可以利用Twig函式patyh(或PHP裡的小工具router)來參照路由設定產生URL,而不是直接將URL寫死在模板裡。之後假設你需要修改特定頁面的URL,你只要更改路由的配置,模板就會自動生成新的URL。

首先,透過以下的路由設定要連結的"welcome"頁面:
// 省略程式碼
要連接到這個頁面,請使用Twig函式path()來使用這個路由:
<a href="{{ path('welcome') }}">Home</a>
如同預期,這會產生URL/。現在,來看看這個更複雜的路由:
// 省略程式碼
在這個例子裡,你需要指定路由名稱(article_show)和參數{slug}的值。使用這個路由,再次訪問前一個章節用到的recent_list.html.twig這個模板,就可以正確地連結到文章:
{# templates/article/recent_list.html.twig #}
{% for article in articles %}
    <a href="{{ path('article_show', {'slug': article.slug}) }}">
        {{ article.title }}
    </a>
{% endfor %}
你也可以用Twig函式url()來產生絕對路徑。
<a href="{{ url('welcome') }}">Home</a>

連結到Assets
模板也經常需要影像、JavaScript、樣式表(stylesheets)和其他assets(e.g. /images/logo.png),但Symfony提供一個更動態的選項:Twig函式asset()

請安裝套件asset來使用這個函式:
composer require symfony/asset
現在可以使用函式asset()
<img src="{{ asset('images/logo.png') }}" alt="Symfony!" />

<link href="{{ asset('css/blog.css') }}" rel="stylesheet" />
函式asset()的主要功能是讓你的應用更方便移植,如果你的專案在網頁伺服器的跟目錄(e.g. http://example.com),所以給予的路徑會像是/images/logo.png。但如果你的專案放在子目錄內(e.g. http://example.com/my_app),所有的assets路徑就會多了一個子目錄(e.g. /my_app/images/logo.png),函式asset()能解決這個問題,它可以藉由確認你的應用路徑,並相應的產生正確的路徑。

透過versionversion_formatjson_manifest_path的設定,函式assets提供多元的緩存破壞(cache busting)技術

如果需要產生assets的絕對路徑,可以使用Twig函式absolute_url(),像是:
<img src="{{ absolute_url(asset('images/logo.png')) }}" alt="Symfony!" />


在Twig引用Stylesheets和JavaScript

沒有任何網站是完全不需要用到Javscript或stylesheets(樣式表),藉由Symfony模板繼承的特點,這些assets可以很優雅地進行管理,

這個章節將教你如何在Symfony中引用樣式表和Javascript資源,如果你對編譯或創建這個assets有興趣的話,請查看『Webpack Encore documentation』,這個工具可以將webpack和其他現代的Javascript工具無縫集成到Symfony應用程序中。

我們新增兩個區塊來管理assets,一個叫做stylesheets,放在標籤head裡;另一個叫javascripts放在鄰近的標籤body裡,這些區塊將包含專案內所有樣式表和JavaScript。
{# templates/base.html.twig #}
<html>
    <head>
        {# ... #}

        {% block stylesheets %}
            <link href="{{ asset('css/main.css') }}" rel="stylesheet" />
        {% endblock %}
    </head>
    <body>
        {# ... #}

        {% block javascripts %}
            <script src="{{ asset('js/main.js') }}"></script>
        {% endblock %}
    </body>
</html>
這看起來跟一般的HTML很像,只是多了{% block %},你需要在子模板內引用其他的樣式表或javascript時是很方便。例如,假設有個聯絡頁面,且只有這個頁面需要引用一個樣式表contact.css,因此在連絡頁面的模板內,會像是:
{# templates/contact/contact.html.twig #}
{% extends 'base.html.twig' %}

{% block stylesheets %}
    {{ parent() }}

    <link href="{{ asset('css/contact.css') }}" rel="stylesheet" />
{% endblock %}

{# ... #}
在子模板哩,你可以覆寫stylesheets區塊,並放了你新的樣式表標籤到區塊內,如果你想要新增父模板區塊的內容(而不是真的取代掉它),你可以用Twig函式parent()來引用基礎模板的stylesheets區塊內所有內容。

你也可以引用在你的程序包Resources/public/內的assets,你需要執行指令php bin/console assets:install target [--symlink],作用是複製(符號鏈接 symlink)檔案到正確的路徑(目標是應用中預設的目錄"public/")。
<link href="{{ asset('bundles/acmedemo/css/contact.css') }}" rel="stylesheet" />
最後結果會是一個包含main.jsmain.csscontact.css兩個stylesheets的頁面。


引用請求、用戶或Session

Symfony在Twig提供一個全域變數app,可以用來取得現在的使用者、請求等等資訊。

請看『How to Access the User, Request, Session & more in Twig via the app Variable』了解更多。


輸出轉義(Output Escaping)

在呈現任何內容時,Twig會自動轉譯輸出,以防你受跨站腳本(Cross Site Scription, XSS)攻擊。

假設description等於I <3 product="" span="" this="">:

<!-- output escaping is on automatically -->
{{ description }} <!-- I &lt;3 this product -->

<!-- disable output escaping with the raw filter -->
{{ description|raw }} <!-- I <3 this product -->
PHP模板不會自動轉義內容。

更多細節請看『How to Escape Output in Templates』。


結語

模板系統只是Symfony眾多工具之一,且它的功能很簡單:讓我們可以給予動態和複雜的HTML輸出,可以讓我們在最後回傳給使用者、寄出email等等。


繼續前進!

沉浸於Symfony其他部分之前,先看看『Symfony4.2入門教學【第六篇】設定(Configuration)』(原文)。


文章原文:
https://symfony.com/doc/4.2/configuration.html

系列文章:

2019年3月23日 星期六

[Vue.js] 開發用和正式環境的參數設定方式(.env)


在開發階段時,通常會用指令 npm run dev 讓專案運作,當內容有任何修正時,就能快速的生效。完成開發後要放到正式環境時,再執行 npm run build 來產生最終的檔案,放到網頁伺服器上使用。

一般在開發階段時的後端API和正式環境用的API可能是不同的,因此可以利用.env來進行設定。

安裝vue-cli可以快速建置專案(安裝方式),在目錄config/裡有兩個檔案 dev.env.jsprod.env.js,顧名思義一個是在dev用的、一個是在production用的。

1. dev.env.js
'use strict'
const merge = require('webpack-merge')
const prodEnv = require('./prod.env')

module.exports = merge(prodEnv, {
  NODE_ENV: '"development"',
  ROOT_API: '"http://localhost/api"'
})

2. prod.env.js
'use strict'
module.exports = {
  NODE_ENV: '"production"',
  ROOT_API: '"https://example.com/api"'
}
在dev時會用 http://localhost/api,在production時會使用https://example.com/api


在VUE專案裡要使用這個參數:
process.env.API_LOCATION
不需要任何import就可以使用,例如:
mounted() {
  console.log(process.env.ROOT_API)
}
最後需要注意,如果已經在執行npm run dev,在dev.env.js修改可能不會被偵測到,請按ctrl+c離開這個指令再執行一次。


資料來源:
https://alligator.io/vuejs/working-with-environment-variables/

[Vue.js] 安裝axios對後端傳送Request (GET/POST/PUT/DELETE)


安裝指令
npm install axios

使用方式
import axios from 'axios'
// use axios in your project
// ...
1. GET
[URL] 字串、API位址
[HEADER] dict,例如:{'Content-Type': 'multipart/form-data'}
[PARAM] dict,例如:{'id': '1'},等同於?id=1
axios.post('[URL]', {headers:[HEADER], params: [PARAM]})
    .then(response => {
        console.log(response);
    })
    .catch(err => {
        console.log(err.response);
    });
2. POST
[DATA] 資料
axios.get('[URL]', [DATA], {headers:[HEADER]})
    .then(response => {
        console.log(response);
    })
    .catch(err => {
        console.log(err.response);
    });
3. PUT
格式與post相同
axios.put('[URL]', [DATA], {headers:[HEADER]})
    .then(response => {
        console.log(response);
    })
    .catch(err => {
        console.log(err.response);
    });
4. DELETE
axios.delete('[URL]', {headers:[HEADER], data:[DATA]})
    .then(response => {
        console.log(response);
    })
    .catch(err => {
        console.log(err.response);
    });

vue-resource 比較不同的是error handling,API回應中通常包含http_statsu、body、data等,success handling回傳的就是標準的回應,但error handling放在response這個key裡面。舉例要取得http_status,在success handling裡是response[http_status],但error handling是err.response[http_status]


[Sublime] SFTP參數說明


sublime的SFTP套件,可以透過sftp等方式與遠端的伺服器連線、檔案同步,如果不習慣用vim的人真的很方便,詳細的步驟請參考『[Sublime] SFTP同步遠端資料夾』,以下說明 sftp-config.json 的常用參數:
type 連線類型
預設為sftp,其他還有ftpftps

host 主機位址
填寫網域名(domain name)或IP。

user 使用者
使用者名稱

password 密碼
非必填欄位,沒填的話也會在連線同步的時候詢問密碼。

port 埠號
非必填欄位,可以指定埠號,未填寫的話會依照連線類型(type)走預設的埠號。

remote_path 遠端伺服器路徑
填寫遠端伺服器中目錄的絕對路徑。

ssh_key_file 金鑰檔案
如果使用ssh key來連線遠端伺服器時,請填寫使用金鑰的檔案位置。
p.s. 路徑中的目錄如果有中文的話,目前嘗試連線會失敗,不確定原因。

save_before_upload 同步前儲存
假設將本地端檔案同步到遠端時,若未儲存的資料,是否直接儲存並上傳,預設值為true

upload_on_save 儲存並同步
預設值為false,決定當本地端的檔案儲存時,是否同時同步上傳遠端伺服器。也就是預設的情況下,本地端的修改不會影響遠端伺服器;反之,當設為true時,本地端的修改會同步上傳遠端的伺服器。

sync_down_on_open 開啟專案時自動下載
當本地端的目錄被開啟時,自動檢查遠端伺服器是否有更新的資料,預設為false,並不會有任何動作;反之,則將更新的檔案下載到本地端。

confirm_downloads 確認下載
下載前詢問使用者是否執行,預設false

confirm_sync 確認同步
同步前詢問使用者是否執行,預設true

ignore_regexes 忽略的目錄或檔案
.gitignore的用途相同,可以用正規表示式(regular expression)來列出規則,決定哪些資料不要進行同步。


資料來源:
https://wbond.net/sublime_packages/sftp/settings



2019年3月1日 星期五

Symfony4.2入門教學【第四篇】控制器(Controllers)


所謂控制器是一個PHP函式,用來讀取Request物件中的資訊,並建立和回傳一個Response物件,這個回應(response)可以是HTML頁面、JSON、XML、供下載的檔案、重新定向(redirect)、404錯誤等等,控制器根據應用的需求,執行任何可能的邏輯去呈現畫面。

如果你還沒建立第一個頁面,請查看文章『建立第一個頁面』之後再回來吧!


簡易的控制器

雖然控制器可以是任何可呼叫的(函式、物件中的方法或Closure),但大多會是一個控制器類內的方法(a method inside a controller class):
// src/Controller/LuckyController.php
namespace App\Controller;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

class LuckyController
{
    /**
     * @Route("/lucky/number/{max}", name="app_lucky_number")
     */
    public function number($max)
    {
        $number = random_int(0, $max);

        return new Response(
            'Lucky number: '.$number.''
        );
    }
}
控制器就是方法number(),被放在名為LuckyController的控制器類。

這個控制器非常容易理解:
  • line 2: Symfony利用PHP的命名空間(namespace)功能來命名整個控制器類。
  • line 4: Symfony也是利用PHP的命名空間功能來命名整個控制器類:關鍵字use輸入(import)一個控制器必要回傳的類(class)Response
  • line 7: 實作上,類名可隨便取,但按慣例會用Controller當作後綴字。
  • line 12: 由於有個{max}通配符在路由,操作方法(action method)會有一個$max的參數。
  • line 16: 控制器建立和回傳一個Response物件

對應URL到控制器
想要看到控制器呈現的結果,你必須透過路由將URL與其對應,這邊的設定是透過註解式路由,寫成@Route("/lucky/number/{max}")來達成對應。

想看頁面,請用瀏覽器到這個URL:
欲見更多關於路由資訊,請看路由



基礎的控制器和服務

Symfony為了讓大家輕鬆點,有一個基礎控制器AbstractController可以選擇使用,你可以延伸了解一些輔助方式(helper methods)。

在控制器類的上方加上use的語句,並修改LuckyController以擴展(extend)它:
// src/Controller/LuckyController.php
namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;

class LuckyController
class LuckyController extends AbstractController
{
    // ...
}
就是這樣!你現在已經接觸了像$this->render()這樣的方法,接下來還有很多其他的方法你會慢慢學到。

生成URL
generateUrl()方法是一個輔助方法,可以針對給定的路由產生URL:
$url = $this->generateUrl('app_lucky_number', ['max' => 10]);

重新導向(Redirecting)
如果你像要將使用者重新導向其他頁面,可以使用redirectToRoute()redirect()兩個方法:
use Symfony\Component\HttpFoundation\RedirectResponse;

// ...
public function index()
{
    // redirects to the "homepage" route
    return $this->redirectToRoute('homepage');

    // redirectToRoute is a shortcut for:
    // return new RedirectResponse($this->generateUrl('homepage'));

    // does a permanent - 301 redirect
    return $this->redirectToRoute('homepage', [], 301);

    // redirect to a route with parameters
    return $this->redirectToRoute('app_lucky_number', ['max' => 10]);

    // redirects to a route and maintains the original query string parameters
    return $this->redirectToRoute('blog_show', $request->query->all());

    // redirects externally
    return $this->redirect('http://symfony.com/doc');
}

redirect()方法並完全不會檢查重新導向的目標,如果你是導向由用戶提供的URL,可能會使得你的應用程式產生未經驗證重新定向的安全漏洞(unvalidated redirects security vulnerability)。

渲染模板(rendering template)
如果你要回傳HTML,你會想要用渲染模板,方法render()會渲染模板,並封裝成Response物件給你:
// renders templates/lucky/number.html.twig
return $this->render('lucky/number.html.twig', ['number' => $number]);
模板和Twig相關的說明,在文章『建立和使用模板』(原文)有更多的說明。

獲取服務(fetching services)
Symfony包含相當多有用的物件,稱為服務(service)。可以用來渲染模板、發送信件、查詢資料庫和任何你想得到的工作。

如果你要在控制器裡使用服務,請用它的類名(或介面(interface))宣告參數,Symfony會自動提供你所需的服務:
use Psr\Log\LoggerInterface;
// ...

/**
 * @Route("/lucky/number/{max}")
 */
public function number($max, LoggerInterface $logger)
{
    $logger->info('We are logging!');
    // ...
}
太棒了!

其他還有哪些服務類型可以使用?如果想知道,請在控制台執行指令debug:autowiring
php bin/console debug:autowiring

如果需要控制參數的確切值,可以使用其名稱綁定(bind)參數:
// 省略程式碼
和其他服務一樣,你可以在控制器中用常規構造函數注入(regular constructor injection)。

想要知道更多關於服務的資訊,請看服務容器(Service Container)這篇文章。


產生控制器

為了節省時間,你可以安裝Symfony Maker,並藉由它產生一個新的控制器類:
php bin/console make:controller BrandNewController

created: src/Controller/BrandNewController.php
如果想要產生針對Doctrine實體(entity)完整的CRUD,執行:
php bin/console make:crud Product

New in version 1.2: MakeBundle 1.2內介紹了指令make:crud


錯誤處理及404頁面

如果找不到東西,應該回傳一個404的回應,請拋(throw)一個特殊形態的例外(exception)來達成:
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

// ...
public function index()
{
    // retrieve the object from database
    $product = ...;
    if (!$product) {
        throw $this->createNotFoundException('The product does not exist');

        // the above is just a shortcut for:
        // throw new NotFoundHttpException('The product does not exist');
    }

    return $this->render(...);
}
方法createNotFoundException()只是一個NotFoundHttpException特殊物件的捷徑,最終會觸發Symfony內的一個404 HTTP回應。

如果你拋一個延伸的例外或HttpException的實例(instance),Symfony會自帶適當的HTTP狀態碼(status code),否則回應就會是HTTP status code 500。
// this exception ultimately generates a 500 status error
throw new \Exception('Something went wrong!');
每個情況下,錯誤頁面(error page)是顯示給一般使用者看,完整的除錯頁面(full debug error page)則是供開發者(i.e. 當你在除錯模式(Debug mode) - The parameters Key: Parameters (Variables))。

若想要客製化顯示給使用者看的錯誤頁面,請參考如何將錯誤頁面客製化的文章(How to Customize Error Pages article)。


回應物件作為控制器參數

如果想要讀取查詢參數、取得請求的標頭(header)或取得上船的檔案?所有的資訊都存在Symfony的Request物件中,要在你的控制器內取得,只要用請求類名宣告成參數:
use Symfony\Component\HttpFoundation\Request;

public function index(Request $request, $firstName, $lastName)
{
    $page = $request->query->get('page', 1);

    // ...
}
更多資訊請參考Request Object(原文)


管理Session

Symfony提供session的服務,可以讓你從請求中取得更多關於使用者的資訊,預設狀態下就可以使用session,不過只有在讀取或寫入的時候才會啟動。

Session儲存區和其他設定都是由config/packages/framework.yaml檔案內的framework.session configuration管控。

要取得session,請新增一個參數並宣告型態為SessionInterface
use Symfony\Component\HttpFoundation\Session\SessionInterface;

public function index(SessionInterface $session)
{
    // stores an attribute for reuse during a later user request
    $session->set('foo', 'bar');

    // gets the attribute set by another controller in another request
    $foobar = $session->get('foobar');

    // uses a default value if the attribute doesn't exist
    $filters = $session->get('filters', []);
}
儲存的屬性會放在session哩,跟使用者其他的session一起。

想要了解更多,請看Session

Flash Messages
你可以儲存一些特別的訊息在使用者的session,稱為"Flash",設計上,flash message只會使用一次: 一旦你檢索後就會自動從session裡面移除,這個特性讓flash message特別適合存放用戶通知。

例如:想像你需要處理一個表單(form)的遞交:
use Symfony\Component\HttpFoundation\Request;

public function update(Request $request)
{
    // ...

    if ($form->isSubmitted() && $form->isValid()) {
        // do some sort of processing

        $this->addFlash(
            'notice',
            'Your changes were saved!'
        );
        // $this->addFlash() is equivalent to $request->getSession()->getFlashBag()->add()

        return $this->redirectToRoute(...);
    }

    return $this->render(...);
}
在處理完這個請求後,控制器會設定flash message到session並重新導向,訊息的鍵(key)(這個例子裡,鍵就是notice)可以任意指定:你可以用這個key來檢索訊息。

在下一頁的模板中(如果放在你的基礎模板中會更好),用app.flashes()來讀取session內任何flash message:
{# templates/base.html.twig #}

{# read and display just one flash message type #}
{% for message in app.flashes('notice') %}
    
{{ message }}
{% endfor %} {# read and display several types of flash messages #} {% for label, messages in app.flashes(['success', 'warning']) %} {% for message in messages %}
{{ message }}
{% endfor %} {% endfor %} {# read and display all flash messages #} {% for label, messages in app.flashes %} {% for message in messages %}
{{ message }}
{% endfor %} {% endfor %}
常見使用noticewarningerror作為不同類型flash message的鍵(key),當然你也可以使用任何符合你需求的鍵。

你可以用方法peek(),替代存起來再檢索訊息。


請求和回應

如同上述,Symfony會傳一個Request物件給任何有宣告參數Request類的控制器:
use Symfony\Component\HttpFoundation\Request;

public function index(Request $request)
{
    $request->isXmlHttpRequest(); // is it an Ajax request?

    $request->getPreferredLanguage(['en', 'fr']);

    // retrieves GET and POST variables respectively
    $request->query->get('page');
    $request->request->get('page');

    // retrieves SERVER variables
    $request->server->get('HTTP_HOST');

    // retrieves an instance of UploadedFile identified by foo
    $request->files->get('foo');

    // retrieves a COOKIE value
    $request->cookies->get('PHPSESSID');

    // retrieves an HTTP request header, with normalized, lowercase keys
    $request->headers->get('host');
    $request->headers->get('content-type');
}
Request類有幾個公開屬性(public properties)和方法,可返回有關請求的所有資訊。

Request一樣,Response物件也有公開屬性headersResponseHeaderBag提供一些好用的方法來設定回應的標頭檔(header),標頭名稱(header names)一般標準使用Content-Type,等同於content-typecontent_type

控制器唯一的條件就是要回傳Response物件:
use Symfony\Component\HttpFoundation\Response;

// creates a simple Response with a 200 status code (the default)
$response = new Response('Hello '.$name, Response::HTTP_OK);

// creates a CSS-response with a 200 status code
$response = new Response('');
$response->headers->set('Content-Type', 'text/css');
有個特殊的類可以讓部分的回應簡單一點,下面會列舉一部份。想要了解更多關於RequestResponse(還有特殊類的Response)的資訊,請查看HttpFoundation component documentation

回傳JSON回應
如果要在控制器內回傳JSON,可以用輔助方法json(),這會回傳一個已經自動編碼(encode)的JsonResponse特殊物件。
// ...
public function index()
{
    // returns '{"username":"jane.doe"}' and sets the proper Content-Type header
    return $this->json(['username' => 'jane.doe']);

    // the shortcut defines three optional arguments
    // return $this->json($data, $status = 200, $headers = [], $context = []);
}
如果能在你的應用裡使用serializer service,可以幫你把資料連載(serialize)成JSON格式,不然也可以用函式json_encode

媒體文件回應(Streaming File Responses)
你可以在控制器內用輔助方式file()操作檔案:
public function download()
{
    // send the file contents and force the browser to download it
    return $this->file('/path/to/some_file.pdf');
}
輔助方式file()提供一些參數設定來決定表現行為:
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;

public function download()
{
    // load the file from the filesystem
    $file = new File('/path/to/some_file.pdf');

    return $this->file($file);

    // rename the downloaded file
    return $this->file($file, 'custom_name.pdf');

    // display the file contents in the browser instead of downloading it
    return $this->file('invoice_3241.pdf', 'my_invoice.pdf', ResponseHeaderBag::DISPOSITION_INLINE);
}


結語

當你建立頁面時,你最後會需要幫頁面寫一些邏輯的程式碼,在Symfony稱之為控制器,是一個PHP的函式能讓你執行任何操作,並回應一個Response物件給使用者。

為了簡化一點,你可能會延伸基礎控制器AbstractController的類,因為這樣可以讓你快速取得方法render()redirectToRoute()的捷徑。

在其他文章裡,你會學到如何在控制器裡使用特定的服務,幫助你從資料庫獲取物件、處理表單提交、處理緩存等等。


繼續前進!

接下來,來學習『Symfony4.2入門教學【第五篇】模板(Templates)』(rendering template with Twig)。


文章原文:
https://symfony.com/doc/4.2/controller.html

系列文章:

2019年2月28日 星期四

Symfony4.2入門教學【第三篇】路由(Routing)


漂亮的URLs是任何嚴謹的網頁應用程式都需要的,這代表比起index.php?article_id=57這樣不美觀的URLs,更喜歡像是/read/intro-to-symfony這個樣子。

保有彈性也是很重要的一點,如果你像要將一個頁面URL從/blog變成/news該怎麼做?為了這個更動,有多少連結要追溯、更新?如果你是用Symfony的路由,這些問題都能輕鬆解決。


建立路由

路由是將URL對應到控制器(controller),假設我們希望有個路由可以對應到/blog,其他更動態的路由可以對應到更多URL,如/blog/my-post/blog/all-about-symfony

路由的設定可以寫在YAML、XML和PHP,所有的檔案類型都能提供相同的特性和效能,所以只要選一個喜歡的使用即可,如果是用PHP註解式宣告(PHP annotations),請執行一次這個指令讓系統支援這個方式。
composer require annotations
現在可以設定路由:
// 省略程式碼
由於這兩個路由:
  • 如果使用者前往/blog,與第一個路由匹配並執行函式list()
  • 如果使用者前往/blog/*,與第二個路由相符並執行函式show(),有個參數$slug會傳到show()去對應。比方說,如果使用者前往/blog/yay-routing,所以$slug等於yay-routing
只要在路由中設定{placeholder},這個部分會變成通配符(wildcard),會配對任何值,現在你的控制器將會有一個$placeholder的參數(通配符和參數名稱必須相符)。

每個路由還有一個內部名稱(internal name):blog_listblog_show},名稱可以自行決定(只要不重複),現在還沒有用到,之後可以用來產生URL

其他格式的路由
每個函式上面的@Route稱為註解(annotation),如果你傾向用YAML、XML或PHP設定路由,也沒關係!在config/資料夾內新建一個路由檔案(e.g. routes.xml),Symfony會自動使用。



本地化路由 (i18n)

路由可以提供每個地區(locale)一個獨一無二的路徑來達到本地化,Symfony提供一個方便的方法去宣告本地化路由,省去許多重複性的工作。
// 省略程式碼
當需求(request)與一個本地化路由相符時,Symfony會自動知道要要使用哪一個地區,這樣的宣告方式也同時降低重複註冊路由的需求,也就是降低因定義不一致產生錯誤的風險。

如果應用使用語言和地區(full language + territory)(e.g. fr_FR, fr_BE),你可以在路由中用語言部分代表,當你想要同一個路由使用同一個語言時,可以避免重複定義多個路徑。

對於一個國際化的應用,在所有路由前加上地區當作前綴是很常見的需求,每一個地區定義不同的前綴就能達成(如果有需要的話,預設地區可以設定空白前綴字):
// 省略程式碼


新增通配符{wildcard}的條件

想像一下有個blog_list的路由是分成很多頁的部落格貼文,路徑可能像是blog/2blog/3,如果將路由的路徑設定成blog/{page},你可能會有疑問:
  • blog_list: blog/{page}blog/*相符
  • blog_show: blog/{slug}也與blog/*相符
當兩個路由和同一個路徑都相符時,會以第一個路由為主,不幸地是這代表/blog/yay-routing將會配對blog_list,這不是我們想要的。

為了解決這個問題,我們新增一個條件(requirement),讓{page}通配符只能是數字(number, digits):
// 省略程式碼
\d+是一個通常表示式(regular expression),能與任何長度的數字配對,整理一下現在的情況:

URL Route Parameters
/blog/2 blog_list $page = 2
/blog/yay-routing blog_show $slug = yay-routing

如果你願意的話,條件可以和佔位符(placeholder)寫在同一行,句法會像這樣{placeholder_name},這個特性可以讓設定檔顯得更加簡潔,不過當條件比較複雜時,也同時降低可讀性。
// 省略程式碼
想要認識更多路由條件 - 像是HTTP method、主機名稱(hostname)和動態表示式(dynamic expressions) - 請看如何設定路由條件


設定占用符{placeholders}預設值

在前面的例子中,blog_list設定的路徑是/blog/{page},如果使用這訪問/blog/1,將會符合;當如果是/blog,將不會配對成功。只要在路徑設定一個{placeholder},就必須有值。

所以如何再一次將讓blog_list/blog配對呢?可以透過設定預設值:
// 省略程式碼
現在,當使用者前往/blog時,會對應到路由blog_list,此時$page為預設值1

如果你希望每次產生URL時都需要預設值(等同前面的舉例,強制讓/blog變成/blog/1),在佔位符前面加上字元!/blog/{!page}

New in version 4.3:
Symfony 4.3中新增了強制在產生的URL中包含預設值的功能。

和路由條件一樣,預設值和佔位符也能將定義寫在同一行,句法為{placehodler_name?defulat_value},這個功能與路由條件能夠同時使用,所以你可以把預設值、路由條件兩者與佔位符用一行定義:
// 省略程式碼
如果想把任何佔位符的預設值定為null,請在?字元後面什麼都不要加。(e.g. /blog/{page?})


列出所有路由

隨著你的專案開發,最終你會有一堆路由,如果要全部檢視請執行:
php bin/console debug:router

------------------------------ -------- -------------------------------------
 Name                           Method   Path
------------------------------ -------- -------------------------------------
 app_lucky_number              ANY    /lucky/number/{max}
 ...
------------------------------ -------- -------------------------------------


進階路由範例

綜合上述,請看下面這個進階範例:
// 省略程式碼
如同你看到的,這個路由只會在{_locale}這個部分是enfr,而且{year}是數字時,才會匹配。這個路由也顯示在佔位符之間如何使用點(dot),而非使用斜槓(slash),符合這個陸游的URL像是:
  • /articles/en/2010/my-post
  • /articles/fr/2010/my-post.rss
  • /articles/en/2013/my-latest-post.html

特殊路由參數_format
這範例也點出特殊路由參數_format,使用這個參數時,匹配的值將成為Request對象的“請求格式”。

最終,請求格式用於諸如設置響應的Content-Type之類的事情(例如,json請求格式轉換為Content-Typeapplication/json)。

有時您希望全局配置路線的某些部分,Symfony利用服務容器參數(service container parameters)為您提供了一種方法。在“如何在路由中使用服務容器參數(How to Use Service Container Parameters in your Routes)”中閱讀有關此內容的更多信息。

特殊的路由參數
就像你看到的,每個路由參數或預設值會成為控制器函式可取得的變數,除此之外,有四個特別的參數:每一個都能在你的應用裡發揮獨有的功能:
_controller
如同所示,當路由配對成功時,決定要執行哪一個控制器。

_format
用來設定請求(request)的格式(閱讀更多)

_fragment
用來設定片段標識符,這是一個自選的功能,在URL的最後加上以#字元開頭的字串,來辨識整份文件的某個段落。

_locale
用來設定請求(request)的地區(閱讀更多)

重新導向尾端斜槓(slashes)的URL
從古到今,URL遵照UNIX協定,如果當作目錄就在尾端加上斜槓(e.g. https://example.com/foo/),視為文件的話會刪掉最後的斜槓來進行導向(e.g. https://example.com/foo,雖然兩個URL可以提供不通的內容,但將兩者視為相同的URL並在它們之間重新轉向是很常見的。

Symfony遵循有和沒有尾端斜槓URL之間的重新導向這樣的邏輯(但只適用GETHEAD請求):

Route path If the requested URL is /foo If the requested URL is /foo/
/foo It matches (200status response) It makes a 301 redirect to /foo
/foo/ It makes a 301 redirect to /foo/ It matches (200status response)

如果應用內定義每個路徑了不同的路由(/foo/foo/),不會發生這種自動重定向的情形,而且總是配對正確的路由。


控制器命名方式

路由內controller格式為CONTROLLER_CLASS::METHOD
若要引用控制器的一種__invoke()的操作,不需要傳函式名稱,或者用完全合格的類名(fully qualified class name)(e.g. App\Controller\BlogController)。


產生URLs

路由系統也可以產生URL,實際上,路由是雙向系統:由URL對應到控制器、路由對應到URL。

要生成URL,你需要指定路徑會使用到的路由的名稱(e.g blog_show)和任何通配符(slug = my-blog-post),有了這些資訊,控制器就能產生URL:
class MainController extends AbstractController
{
    public function show($slug)
    {
        // ...

        // /blog/my-blog-post
        $url = $this->generateUrl(
            'blog_show',
            ['slug' => 'my-blog-post']
        );
    }
}
如果你需要從服務(service)產生URL,型態會像是UrlGeneratorInterface
// src/Service/SomeService.php

use Symfony\Component\Routing\Generator\UrlGeneratorInterface;

class SomeService
{
    private $router;

    public function __construct(UrlGeneratorInterface $router)
    {
        $this->router = $router;
    }

    public function someMethod()
    {
        $url = $this->router->generate(
            'blog_show',
            ['slug' => 'my-blog-post']
        );
        // ...
    }
}

產生帶有查詢字串的URL
generate()的函式會使用整個通配符陣列(an array of wildcard value)來闡產生URL,但如果傳送了額外的值,會當作查詢字串加到URL。
$this->router->generate('blog', [
    'page' => 2,
    'category' => 'Symfony',
]);
// /blog/2?category=Symfony

產生本地化URLs
當路由是本地的(localized),Symfony會用現在的請求地區(request locale)來生成URL,如果要生成不同地區的URL,就必須傳_locale到參數陣列內。
$this->router->generate('about_us', [
    '_locale' => 'nl',
]);
// generates: /over-ons

在模板(Template)裡產生URLs
在Twig產生URLs,可以參考關於模板的文章:連結到頁面(Linking to Pages),假設你也想要在JavaScript裡產生連結,請看:如何在JavaScript產生路由URLs(How to Generate Routing URLs in JavaSript)。

產生絕對路徑(Absolute URLs)
一般情況下,路由會產生相應的URLs(e.g. /blog),從控制器,將UrlGeneratorInterface::ABSOLUTE_URL當作generateUrl()的第三個參數:
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;

$this->generateUrl('blog_show', ['slug' => 'my-blog-post'], UrlGeneratorInterface::ABSOLUTE_URL);
// http://www.example.com/blog/my-blog-post
產生絕對URL時使用的主機是透過當前Request對象自動檢測的。從Web上下文(context)外部產收絕對URL時(例如在控制台命令中),這不起作用。請參閱如何從控制台生成URL(How to Generate URLs from the Console)以了解如何解決此問題。


Troubleshooting

當你在寫路由時,你可能看過這些常見的錯誤:
Controller "App\Controller\BlogController::show()" requires that you provide a value for the "$slug" argument.
這發生在你的控制器函式需要一個參數(e.g.$slug):
public function show($slug)
{
    // ..
}
但是你的路由裡沒有{slug}這個通配符(e.g. /blog/show),在路由新增{slug}/blog/show/{slug}或將參數定一個預設值(i.e. $slug = null)。
Some mandatory parameters are missing ("slug") to generate a URL for route "blog_show".
這代表你正試圖讓blog_show路由產生一個沒有slug值的URL(但slug值卻是必要的,因為路徑中有個通配符{slug}),為了修正這個問題,產生路由時請傳slug值:
$this->generateUrl('blog_show', ['slug' => 'slug-value']);

// or, in Twig
// {{ path('blog_show', {'slug': 'slug-value'}) }}


繼續前進!

路由沒問題了,現在一起揭開控制器(原文)的強大吧!



文章原文:
https://symfony.com/doc/4.2/setup.html

系列文章:

2019年2月13日 星期三

Symfony4.2入門教學【第二篇】建立第一個頁面


建立一個新頁面,有兩個步驟要做:
  1. 建立路由(route):所謂路由就是連接到網頁的URL(e.g. /about)和控制器。
  2. 建立控制器(controller):控制器是由PHP函式構成,針對你接收到請求(request)去建立一個Symfony的回應(Response),這個回應有可能是HTML、JSON字串或二進位檔(binary file)如圖片或PDF。

你更傾向影片教學?查看教學影片

Symfony 有包含HTTP 請求-回應生命週期(HTTP Request-Response lifecycle),要了解更多訊息,請看Symfony和HTTP基礎知識


新建頁面:路由和控制器(Route and Controller)

繼續之前,請先確定你已經讀完「Symfony4.2入門教學【第一篇】安裝」(原文)這篇文章,且可以用瀏覽器觀看你的Symfony應用。

架設你要建立一個頁面 - /lucky/number - 會亂數產生一個幸運數字並印在頁面上,為了達到這個目的,新建一個"Controller Class"、一個"controller"的函式(method)在class裡面。
<?php
// src/Controller/LuckyController.php
namespace App\Controller;

use Symfony\Component\HttpFoundation\Response;

class LuckyController
{
    public function number()
    {
        $number = random_int(0, 100);

        return new Response(
            'Lucky number: '.$number.''
        );
    }
}
現在你需要將這個控制器與一個可以公開存取的URL路徑(e.g. /lucky/number)連接在一起,讓使用者訪問這個頁面時能執行number()這個函式,這個連接的方式必須在檔案config/routes.yaml建立一個路由來定義。
# config/routes.yaml

# the "app_lucky_number" route name is not important yet
app_lucky_number:
    path: /lucky/number
    controller: App\Controller\LuckyController::number
就是這麼簡單!如果你已經啟動了Symfony的網頁伺服器,可以打開瀏覽器去看看:
http://localhost:8000/lucky/number
若你看到一個幸運數字印在頁面上,那就恭喜你囉!但在你跑去買樂透之前,先看看這是怎麼辦到的呢,建立頁面的兩個步驟記得嗎?
  1. 建立路由:在config/routes.yaml檔案裡,路由定義了頁面的URL要呼叫哪一個控制器,你可以在路由這一個章節學到更多,裡面也會提到如何設定參數(Variable)
  2. 建立控制器:在此建立頁面的函式,並在最後回覆一個Response的物件,你可以在控制器的章節內學到更多,裡面也會提到如何將JSON字串當作Response回傳。


註解式路由(Annotation Routes)

如果不是在YAML定義路由,取得代之,Symfony提供一個註解式(Annotation)的路由,如果要使用,請先安裝套件:
composer require annotations
你可以直接在控制器的函式上方定義路由:
// src/Controller/LuckyController.php

// ...
use Symfony\Component\Routing\Annotation\Route;

class LuckyController
{
   /**
    * @Route("/lucky/number")
    */
    public function number()
    {
        // this looks exactly the same
    }
}
是不是很簡單呢!這個頁面 -http://localhost:8000/lucky/number- 功能就跟之前一樣,Annotations絕對是個備受推薦的路由設定方法。


Auto-Installing Recipes with Symfony Flex

你可能還沒有發現,當你執行composer require annotations這個指令時,發生了兩件神奇的事情,這都是多虧了強大的Composer插件 Flex

首先,annotations並非套件真正的名稱,而是一個別名(alias)(i.e. 捷徑shortcut),是由Flex解析成實際的套件sensio/framework-extra-bundle

第二,當這個套件完成下載後,Flex會自動執行食譜(Recipe),所謂食譜就是一連串指令去告訴Symfony如何統整這些外部套件。Flex食譜使得許多套件能辦到更多事情,像是新增設定檔、新增目錄、更新.gitignore,以及在.env新增設定,Flex讓安裝自動化,這麼以來你就可以專注在寫程式上。

你可以參考"Using Symfony Flex to Manage Symfony Applications"了解更多資訊,不過既然你安裝新的套件時,Flex會自動地在背景執行它的任務,這就不是那麼重要了!


指令 bin/console

你的專案已經內建了相當好用的除錯工具(debugging tool):指令bin/console,試著執行看看:
php bin/console
你應該會看到一堆指令,可以提供除錯資訊,可以幫你產生程式、產生資料庫遷移(database migration)等等,當你安裝更多套件時,可以接觸到更多指令。

若要取得伺服器內所有路由,請執行指令debug:router
php bin/console debug:router
你應該會看到app_lucky_number在最上面:

Name Method Scheme Host Path
app_lucky_number ANY ANY ANY /lucky/number

你也會看到一些除錯用的路由(debugging route)在app_lucky_number下面,關於除錯的路由會在下一個章節更詳細的介紹。

繼續往下學,就能了解更多指令。


除錯工具列

除錯工具列(Web Debug Toolbar)是Symfony最大的特色,當你在開發時,頁面底下在這個區域會顯示大量的有助於除錯的資訊,全都包由symfony/profiler-pack這個套件來搞定。

在頁面的最下方看到都一個黑色框框,你會慢慢更了解上面訊息代表的意思,你可以隨意試試,每個icon都可以滑過去或按按看,可以看到關於路由、效能、logging等等的資訊。


Rendering a Template

如果想要讓控制器回傳一個HTML物件,你或許會希望回應(render)一個模板。幸運的是,Symfony有Twig,這個模板語言非常簡單、強大且真的很有趣。

請確定LuckyController必須延伸(extend)Symfony的class AbstractController
// src/Controller/LuckyController.php

// ...
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;

class LuckyController
class LuckyController extends AbstractController
{
    // ...
}
現在,render()這個方便的函式可以回傳一個模板,給一個number的變數讓你可以在Twig中使用。
// src/Controller/LuckyController.php

// ...
class LuckyController extends AbstractController
{
    /**
     * @Route("/lucky/number")
     */
    public function number()
    {
        $number = random_int(0, 100);

        return $this->render('lucky/number.html.twig', [
            'number' => $number,
        ]);
    }
}
模板檔案放在templates/目錄裡,這個目錄在安裝Twig時會自動建立,現在建立一個新目錄templates/lucky和一個新檔案number.html.twig在目錄裡:
{# templates/lucky/number.html.twig #}

<h1>Your lucky number is {{ number }}</h1>
這個{{ number }}語句是用來在Twig裡印出參數,重新整理網頁就能看到新的幸運數字。
http://localhost:8000/lucky/number
再來你可能會疑惑,網頁的除錯工具列(the Web Debug Toolbar)是不是不見了呢?這是因為現在的模板裡缺了</body>這個tag,你可自已加上body,或著延伸base.html.twig,裡面已經包含所有基礎的HTML元件。

建立及使用模板的文章內,你可以如何寫迴圈、render其他模板和利用其強大的佈局繼承系統。


查看專案架構

有個好消息,就是你已經實際操作過專案中最重要的目錄:

config/
當然是包含了設定檔,你可以設定路由、服務(services)和套件。

src/
所有的PHP程式碼都放在這裡。

templates/
所有Twig模板存放的位置。

大部分的情況下,你只會用到src/templates/config/,繼續看下去就可以更加了解這幾個目錄各別的用途。

bin/
大名鼎鼎的bin/console就在這裡,還有一些其他執行檔(executable files)。

var/
這裡的檔案都是自動產生存在這裡,像是暫存(cache)檔案(var/cache/)和log(var/log/)。

vendor/
第三方函式庫的位置,用Composer安裝的套件會自動載到這邊。

public/
這是你專案的根目錄,任何可以開放存取的檔案可以放在這裡。

當你安裝新的套件時,如果有需要會自動建立新的目錄。


下一步

恭喜你!你已經掌握Symfony,並學到如何建立一個架構優雅、具功能的、快速的、便於維護的全新方法。

好的,是時候來看看這些文章掌握基礎知識:
接著,其他一些重要的主題也值得一學,像是service container, the form system, using Doctrine (if you need to query a database)等等。

Have fun!


文章原文:
https://symfony.com/doc/4.2/page_creation.html

系列文章:

Symfony4.2入門教學【第一篇】安裝


你更傾向影片教學?查看教學影片

建立新的Symfony應用,首先請確認你使用PHP 7.1或更高的版本,並安裝composer。如果還沒,請先在系統上安裝composer,如果想要使用虛擬機器(Virtual Machine, VM),請查看Homestead

建立新的專案執行指令:
composer create-project symfony/website-skeleton my-project

這將會建立一個新的my-project目錄,在裡面下載一些相依套件,並產生你所需的基本資料夾和檔案。換句話說,你的應用程式已經準備好了。

website-skeleton。如果你想要建立規模小一點的服務,例如console applications或APIs,可以考慮使用更簡易的skeleton專案。
composer create-project symfony/skeleton my-project

# optional: install the web server bundle (explained next)
cd my-project
composer require symfony/web-server-bundle --dev


執行Symfony應用

在正式環境,我們應該利用網頁伺服器軟體,如Nginx或Apache(參考如何使用網頁伺服器),但在開發環境時,使用Symfony的PHP網頁伺服器是更方便的方法。

進到新專案的目錄下,啟動伺服器:
cd my-project
php bin/console server:run

打開瀏覽器輸入http://localhost:8000/,如果一切順利的話,就會看到預設的首頁。稍後,如果你開發工作告一段落,請可以在終端機按Ctrl+C來關閉伺服器。

如果你在啟動Symfony專案遇到問題,有可能你的系統缺少一些必要的安裝,可以使用工具Symfony Requirements Checker,來確認系統已經準備完畢。

如果你使用的是VM,你可以透過執行下面指令,讓伺服器綁定(bind)所有IP位址:
php bin/console server:start 0.0.0.0:8000
目錄可以透過網路存取的情況下,這台機器不建議接收所有介面(listen to all interfaces)。


用git管理專案

就像多數的程式專案一樣,你可以將專案儲存到像是GitHub、GitLab和Bitbucket的服務上。用指令Git初始本地端儲存庫(Repository),你就可以將程式推(push)到遠端儲存庫。
git init
git add .
git commit -m "Initial commit"
Symfony專案已經建好.gitignore的檔案,當你安裝更多套件時,系統會透過插件Flex(原文)自動去編寫、更新。


現有的Symfony專案設定方式

如果你想編寫一個現有Symfony專案,你只要先取得這個專案,再用composer安裝所需的套件。假設你的工作團隊有使用Git,可以用下面的指令來建立你的專案:
# clone the project to download its contents
cd projects/
git clone ...

# make Composer install the projects dependencies into vendor/
cd my-project/
composer install
你大概也需要檔案.env設定環境參數以符合你的系統需求,或另外做一些專案特有的項目(像是建立資料庫schema)。當你第一次編寫一個現有的Symfony專案,執行下面的指令會顯示這個應用的相關資訊,對你可能會有很大的幫助:
php bin/console about


確認安全漏洞

Symfony提供"Security Checker"這個工具,能確認你的專案相依套件中是否有任何已知的安全漏洞,請查看Security Checker的說明來建置。


The Symfony Demo Application

The Symfony Demo Application完整展現了最合適、推薦的開發方式,是一個功能齊全的應用,在程式碼也有相應的註解和有用的建議,對於Symfony的初學者能當作很好的學習工具。

想要查看程式碼並在本機端建立這個專案,請參考symfony/symfony-demo


開始寫程式

前置作業都完成了,就可以馬上開始建立第一個頁面(原文)囉!


文章原文:
https://symfony.com/doc/4.2/setup.html

系列文章:

2019年2月10日 星期日

[Symfony] ubuntu18安裝symfony 4.2教學


上次寫的這篇「[Symfony] Ubuntu16+PHP7 安裝Synfony3.4」,是用指令 symfony 新建專案,例如 symfony new my_project 3.4 會建立一個版本3.4的專案,不過最新的4.2沒辦法用這個方法,因此誕生了這篇文章,另外作業系統也改用ubuntu18囉!

一、安裝php及相關套件
安裝php(>=7.1.3),ubuntu18內建為7.2。[版本需求]
sudo apt install php

這邊的套件則是symfony專案所需
php-xml php-curl php-mbstring php-zip unzip

二、安裝composer
安裝方法在這邊寫得很詳細,如果是作業系統是windows也有說明,下面則是整理了ubuntu的安裝方法,二擇一即可。(目前是v1.8.3)

[方法一]
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php -r "if (hash_file('sha384', 'composer-setup.php') === '48e3236262b34d30969dca3c37281b3b4bbe3221bda826ac6a9a62d6444cdb0dcd0615698a5cbe587c3f0fe57a54d8f5') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
php composer-setup.php
php -r "unlink('composer-setup.php');"

WARNING: Please do not redistribute the install code. It will change with every version of the installer. Instead, please link to this page or check how to install Composer programmatically.
這一段話是要提醒[方法一]可能會因為版本更新而改變,如果想要安裝最新版本的composer的話,會建議直接到官方這個頁面查看指令,或者使用[方法二],也就是腳本(script)來執行。

[方法二]
開一個新的空白檔案install.sh,增加執行(x)權限。
touch install.sh
chmod +x install.sh

install.sh的檔案內容
#!/bin/sh

EXPECTED_SIGNATURE="$(wget -q -O - https://composer.github.io/installer.sig)"
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
ACTUAL_SIGNATURE="$(php -r "echo hash_file('sha384', 'composer-setup.php');")"

if [ "$EXPECTED_SIGNATURE" != "$ACTUAL_SIGNATURE" ]
then
    >&2 echo 'ERROR: Invalid installer signature'
    rm composer-setup.php
    exit 1
fi

php composer-setup.php --quiet
RESULT=$?
rm composer-setup.php
exit $RESULT

接著只要執行就可以了,如果成功的話應該不會看到任何訊息,沒有消息就是好消息XD
./install.sh

這個方法可以直接安裝最新的composer,想參考更完整的說明可以看這邊

兩個方法二擇一完成之後,這時候目前的目錄下應該會有一個composer.phar的檔案,執行下面的指令,接著就可以直接在全域使用composer。
sudo cp composer.phar /bin/composer

三、新建symfony專案
建立一個名字為my-project的新專案,如果前面的步驟確實的話,會自動執行composer install完成所有的安裝。
composer create-project symfony/website-skeleton my-project

可以來看看專案囉!
cd my-project/
php bin/console server:run
預設為http://127.0.0.1:8000,打開瀏覽器看到畫面就算大功告成了!





如果安裝遇到問題,可以利用下面的方法確認。
composer require symfony/requirements-checker

這時候專案目錄public內會多一個check.php的檔案,可以打開http://127.0.0.1:8000/check.php確認可能的問題在哪,參考畫面如下:


當你排除上面所說的問題後,可以用指令來移除check.php等相關的檔案,以防透漏不必要或機密的資運給網站的訪客。
composer remove symfony/requirements-checker

參考資料:
https://symfony.com/doc/current/setup.html
https://symfony.com/doc/current/reference/requirements.html