<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                [TOC] # 教程:INVO 在第二篇教程中,我們將解釋一個更完整的應用程序,以便更深入地了解使用Phalcon進行開發。INVO是我們創建的示例應用程序之一。INVO是一個小型網站,允許用戶生成發票并執行其他任務,例如管理客戶和產品。您可以從[Github](https://github.com/phalcon/invo)克隆其代碼。 INVO是使用客戶端框架[Bootstrap](http://getbootstrap.com/)制作的。雖然應用程序不會生成實際的發票,但它仍然可以作為一個示例來說明框架的工作原理。 ## 項目結構 在文檔根目錄中克隆項目后,您將看到以下結構: ```bash invo/ app/ config/ controllers/ forms/ library/ logs/ models/ plugins/ views/ cache/ volt/ docs/ public/ css/ fonts/ js/ schemas/ ``` 如您所知,Phalcon沒有為應用程序開發強加特定的文件結構。該項目具有簡單的MVC結構和更目錄public。 在瀏覽器`http://localhost/invo`中打開應用程序后,您將看到如下內容: ![](https://docs.phalconphp.com/images/content/tutorial-invo-1.png) 該應用程序分為兩個部分:前端和后端。前端是一個公共區域,訪客可以接收有關INVO的信息并請求聯系信息。后端是一個管理區域,注冊用戶可以在其中管理其產品和客戶。 ## 路由 INVO使用與Router組件內置的標準路由。這些路由符合以下模式:`/:controller/:action/:params`。這意味著URI的第一部分是控制器,第二部分是控制器動作,其余部分是參數。 以下路由`/session/register`執行控制器`SessionController`及其操作`registerAction`。 ## 配置 INVO有一個配置文件,用于設置應用程序中的常規參數。該文件位于`app/config/config.ini`,并加載到應用程序引導程序的第一行(`public/index.php`): ```php <?php use Phalcon\Config\Adapter\Ini as ConfigIni; // ... // Read the configuration $config = new ConfigIni( APP_PATH . 'app/config/config.ini' ); ``` Phalcon Config(`Phalcon\Config`)允許我們以面向對象的方式操作文件。在這個例子中,我們使用ini文件進行配置,但Phalcon也有其他文件類型的`適配器`。配置文件包含以下設置: ```ini [database] host = localhost username = root password = secret name = invo [application] controllersDir = app/controllers/ modelsDir = app/models/ viewsDir = app/views/ pluginsDir = app/plugins/ formsDir = app/forms/ libraryDir = app/library/ baseUri = /invo/ ``` Phalcon沒有任何預定義的設置約定。章節可以幫助我們根據需要組織選項。在此文件中,稍后將使用兩個部分:`application`和`database`。 ## 自動加載 引導文件(`public/index.php`)中出現的第二部分是自動加載器: ```php <?php /** * Auto-loader configuration */ require APP_PATH . 'app/config/loader.php'; ``` 自動加載器注冊一組目錄,應用程序將在其中查找最終需要的類。 ```php <?php $loader = new Phalcon\Loader(); // We're a registering a set of directories taken from the configuration file $loader->registerDirs( [ APP_PATH . $config->application->controllersDir, APP_PATH . $config->application->pluginsDir, APP_PATH . $config->application->libraryDir, APP_PATH . $config->application->modelsDir, APP_PATH . $config->application->formsDir, ] ); $loader->register(); ``` 請注意,上面的代碼已注冊配置文件中定義的目錄。唯一沒有注冊的目錄是viewsDir,因為它包含HTML + PHP文件但沒有類。另外,請注意我們使用一個名為APP_PATH的常量。此常量在bootstrap(`public/index.php`)中定義,以允許我們引用項目的根目錄: ```php <?php // ... define( 'APP_PATH', realpath('..') . '/' ); ``` ## 注冊服務 引導程序中需要的另一個文件是(`app/config/services.php`)。該文件允許我們組織INVO使用的服務。 ```php <?php /** * Load application services */ require APP_PATH . 'app/config/services.php'; ``` 使用閉包來實現服務注冊,以便延遲加載所需的組件: ```php <?php use Phalcon\Mvc\Url as UrlProvider; // ... /** * The URL component is used to generate all kind of URLs in the application */ $di->set( 'url', function () use ($config) { $url = new UrlProvider(); $url->setBaseUri( $config->application->baseUri ); return $url; } ); ``` 我們稍后會深入討論這個文件。 ## 處理請求 如果我們跳到文件的末尾(`public/index.php`),請求最終由`Phalcon\Mvc\Application`處理,它初始化并執行使應用程序運行所需的所有內容: ```php <?php use Phalcon\Mvc\Application; // ... $application = new Application($di); $response = $application->handle(); $response->send(); ``` ## 依賴注入 在上面代碼塊的第一行中,Application類構造函數接收變量`$di`作為參數。這個變量的目的是什么?Phalcon是一個高度分離的框架,因此我們需要一個充當膠水的組件,以使一切工作在一起。該組件是`Phalcon\Di`.它是一個服務容器,它還執行依賴注入和服務定位,實例化應用程序所需的所有組件。 在容器中注冊服務的方法有很多種。在INVO中,大多數服務已使用匿名函數/閉包進行注冊。由于這個原因,對象以惰性方式實例化,減少了應用程序所需的資源。 例如,在以下摘錄中注冊會話服務。只有在應用程序需要訪問會話數據時才會調用匿名函數: ```php <?php use Phalcon\Session\Adapter\Files as Session; // ... // Start the session the first time a component requests the session service $di->set( 'session', function () { $session = new Session(); $session->start(); return $session; } ); ``` 在這里,我們可以自由更改適配器,執行額外的初始化等等。請注意,該服務是使用名稱`session`注冊的。這是一種允許框架識別服務容器中的活動服務的約定。 請求可以使用許多服務,并且單獨注冊每個服務可能是一項繁瑣的任務。因此,該框架提供了一個名為`Phalcon\Di\FactoryDefault`的`Phalcon\Di`變體,其任務是注冊提供全棧框架的所有服務。 ```php <?php use Phalcon\Di\FactoryDefault; // ... // The FactoryDefault Dependency Injector automatically registers the // right services providing a full-stack framework $di = new FactoryDefault(); ``` 它將大多數服務與框架提供的組件作為標準注冊。如果我們需要覆蓋某些服務的定義,我們可以像上面使用`session`或`url`一樣重新設置它。這就是變量`$di`存在的原因。 ## 登錄應用程序 登錄工具將允許我們處理后端控制器。后端控制器和前端控制器之間的分離是合乎邏輯的。所有控制器都位于同一目錄(`app/controllers/`)中。 要進入系統,用戶必須擁有有效的用戶名和密碼。用戶存儲在數據庫`invo`中的表 `users`中。 在我們開始會話之前,我們需要在應用程序中配置與數據庫的連接。在服務容器中使用連接信息設置名為`db`的服務。與自動加載器一樣,我們再次從配置文件中獲取參數以配置服務: ```php <?php use Phalcon\Db\Adapter\Pdo\Mysql as DbAdapter; // ... // Database connection is created based on parameters defined in the configuration file $di->set( 'db', function () use ($config) { return new DbAdapter( [ 'host' => $config->database->host, 'username' => $config->database->username, 'password' => $config->database->password, 'dbname' => $config->database->name, ] ); } ); ``` 在這里,我們返回一個MySQL連接適配器的實例。如果需要,您可以執行額外操作,例如添加記錄器,分析器或更改適配器,根據需要進行設置。 以下簡單表單(`app/views/session/index.volt`)請求登錄信息。我們刪除了一些HTML代碼,以使示例更簡潔: ```twig {{ form('session/start') }} <fieldset> <div> <label for='email'> Username/Email </label> <div> {{ text_field('email') }} </div> </div> <div> <label for='password'> Password </label> <div> {{ password_field('password') }} </div> </div> <div> {{ submit_button('Login') }} </div> </fieldset> {{ endForm() }} ``` 我們開始使用`Volt`,而不是使用原始PHP作為上一個教程。這是一個內置的模板引擎,受`Jinja`的啟發,提供了一種更簡單友好的語法來創建模板。在你熟悉`Volt`之前不會花太多時間。 `SessionController::startAction`函數(`app/controllers/SessionController.php`)的任務是驗證表單中輸入的數據,包括檢查數據庫中的有效用戶: ```php <?php class SessionController extends ControllerBase { // ... private function _registerSession($user) { $this->session->set( 'auth', [ 'id' => $user->id, 'name' => $user->name, ] ); } /** * This action authenticate and logs a user into the application */ public function startAction() { if ($this->request->isPost()) { // Get the data from the user $email = $this->request->getPost('email'); $password = $this->request->getPost('password'); // Find the user in the database $user = Users::findFirst( [ "(email = :email: OR username = :email:) AND password = :password: AND active = 'Y'", 'bind' => [ 'email' => $email, 'password' => sha1($password), ] ] ); if ($user !== false) { $this->_registerSession($user); $this->flash->success( 'Welcome ' . $user->name ); // Forward to the 'invoices' controller if the user is valid return $this->dispatcher->forward( [ 'controller' => 'invoices', 'action' => 'index', ] ); } $this->flash->error( 'Wrong email/password' ); } // Forward to the login form again return $this->dispatcher->forward( [ 'controller' => 'session', 'action' => 'index', ] ); } } ``` 為簡單起見,我們使用`sha1`在數據庫中存儲密碼哈希,但是,在實際應用中不建議使用此算法,而是使用`bcrypt`。 請注意,在控制器中訪問多個公共屬性,如:`$this->flash`,`$this->request` 或`$this->session`。這些是早期服務容器中定義的服務(`app/config/services.php`)。當它們第一次被訪問時,它們作為控制器的一部分被注入。這些服務是`共享`的,這意味著無論我們調用它們的位置如何,我們總是訪問同一個實例。例如,這里我們調用`session`服務,然后將用戶身份存儲在變量`auth`中: ```php <?php $this->session->set( 'auth', [ 'id' => $user->id, 'name' => $user->name, ] ); ``` 本節的另一個重要方面是如何將用戶驗證為有效用戶,首先我們驗證請求是否已使用方法`POST`進行: ```php <?php if ($this->request->isPost()) { // ... } ``` 然后,我們從表單中接收參數: ```php <?php $email = $this->request->getPost('email'); $password = $this->request->getPost('password'); ``` 現在,我們必須檢查是否有一個用戶具有相同的用戶名或電子郵件和密碼: ```php <?php $user = Users::findFirst( [ "(email = :email: OR username = :email:) AND password = :password: AND active = 'Y'", 'bind' => [ 'email' => $email, 'password' => sha1($password), ] ] ); ``` 注意,使用'bound parameters',占位符`:email:`和`:password:`放置在值應該的位置,然后使用參數`bind`綁定值。這可以安全地替換這些列的值,而不會有SQL注入的風險。 如果用戶有效,我們會在會話中注冊并將他/她轉發到dashboard: ```php <?php if ($user !== false) { $this->_registerSession($user); $this->flash->success( 'Welcome ' . $user->name ); return $this->dispatcher->forward( [ 'controller' => 'invoices', 'action' => 'index', ] ); } ``` 如果用戶不存在,我們會再次將用戶轉發回顯示表單的操作: ```php <?php return $this->dispatcher->forward( [ 'controller' => 'session', 'action' => 'index', ] ); ``` ## 后端安全 后端是一個私有區域,只有注冊用戶才能訪問。因此,有必要檢查只有注冊用戶才能訪問這些控制器。如果您沒有登錄到應用程序并嘗試訪問,例如,產品控制器(私有),您將看到如下屏幕: ![](https://docs.phalconphp.com/images/content/tutorial-invo-2.png) 每當有人嘗試訪問任何控制器/操作時,應用程序都會驗證當前角色(在session中)是否可以訪問它,否則它會顯示如上所述的消息并將流轉發到主頁。 現在讓我們看看應用程序如何實現這一點。首先要知道的是,有一個名為`Dispatcher`的組件。它被告知`Routing`組件找到的路由。然后,它負責加載適當的控制器并執行相應的操作方法。 通常,框架會自動創建`Dispatcher`。在我們的例子中,我們希望在執行所需操作之前執行驗證,檢查用戶是否可以訪問它。為此,我們通過在引導程序中創建函數來替換組件: ```php <?php use Phalcon\Mvc\Dispatcher; // ... /** * MVC dispatcher */ $di->set( 'dispatcher', function () { // ... $dispatcher = new Dispatcher(); return $dispatcher; } ); ``` 我們現在可以完全控制應用程序中使用的Dispatcher。框架中的許多組件觸發允許我們修改其內部操作流程的事件。由于依賴注入組件充當組件的粘合劑,因此名為`EventsManager`的新組件允許我們攔截組件生成的事件,將事件路由到偵聽器。 ### 事件管理 `EventsManager`允許我們將偵聽器附加到特定類型的事件。我們現在感興趣的類型是“dispatch”。以下代碼過濾`Dispatcher`生成的所有事件: ```php <?php use Phalcon\Mvc\Dispatcher; use Phalcon\Events\Manager as EventsManager; $di->set( 'dispatcher', function () { // Create an events manager $eventsManager = new EventsManager(); // Listen for events produced in the dispatcher using the Security plugin $eventsManager->attach( 'dispatch:beforeExecuteRoute', new SecurityPlugin() ); // Handle exceptions and not-found exceptions using NotFoundPlugin $eventsManager->attach( 'dispatch:beforeException', new NotFoundPlugin() ); $dispatcher = new Dispatcher(); // Assign the events manager to the dispatcher $dispatcher->setEventsManager($eventsManager); return $dispatcher; } ); ``` 當觸發一個名為`beforeExecuteRoute`的事件時,將通知以下插件: ```php <?php /** * Check if the user is allowed to access certain action using the SecurityPlugin */ $eventsManager->attach( 'dispatch:beforeExecuteRoute', new SecurityPlugin() ); ``` 觸發`beforeException`時,會通知其他插件: ```php <?php /** * Handle exceptions and not-found exceptions using NotFoundPlugin */ $eventsManager->attach( 'dispatch:beforeException', new NotFoundPlugin() ); ``` SecurityPlugin是一個位于(`app/plugins/SecurityPlugin.php`)的類。此類實現`beforeExecuteRoute`之前的方法。這與Dispatcher中生成的事件之一相同: ```php <?php use Phalcon\Events\Event; use Phalcon\Mvc\User\Plugin; use Phalcon\Mvc\Dispatcher; class SecurityPlugin extends Plugin { // ... public function beforeExecuteRoute(Event $event, Dispatcher $dispatcher) { // ... } } ``` 鉤子事件總是接收第一個參數,該參數包含所生成事件的上下文信息(`$event`),第二個參數是生成事件本身的對象(`$dispatcher`)。插件擴展`Phalcon\Mvc\User\Plugin`類并不是必須的,但通過這樣做,他們可以更輕松地訪問應用程序中可用的服務。 現在,我們將驗證當前會話中的角色,檢查用戶是否具有使用ACL列表的訪問權限。如果用戶沒有訪問權限,我們會重定向到主屏幕,如前所述: ```php <?php use Phalcon\Acl; use Phalcon\Events\Event; use Phalcon\Mvc\User\Plugin; use Phalcon\Mvc\Dispatcher; class SecurityPlugin extends Plugin { // ... public function beforeExecuteRoute(Event $event, Dispatcher $dispatcher) { // Check whether the 'auth' variable exists in session to define the active role $auth = $this->session->get('auth'); if (!$auth) { $role = 'Guests'; } else { $role = 'Users'; } // Take the active controller/action from the dispatcher $controller = $dispatcher->getControllerName(); $action = $dispatcher->getActionName(); // Obtain the ACL list $acl = $this->getAcl(); // Check if the Role have access to the controller (resource) $allowed = $acl->isAllowed($role, $controller, $action); if (!$allowed) { // If he doesn't have access forward him to the index controller $this->flash->error( "You don't have access to this module" ); $dispatcher->forward( [ 'controller' => 'index', 'action' => 'index', ] ); // Returning 'false' we tell to the dispatcher to stop the current operation return false; } } } ``` ### 獲取ACL列表 在上面的例子中,我們使用`$this->getAcl()`方法獲得了ACL。此方法也在插件中實現。現在我們將逐步解釋如何構建訪問控制列表(ACL): ```php <?php use Phalcon\Acl; use Phalcon\Acl\Role; use Phalcon\Acl\Adapter\Memory as AclList; // Create the ACL $acl = new AclList(); // The default action is DENY access $acl->setDefaultAction( Acl::DENY ); // Register two roles, Users is registered users // and guests are users without a defined identity $roles = [ 'users' => new Role('Users'), 'guests' => new Role('Guests'), ]; foreach ($roles as $role) { $acl->addRole($role); } ``` 現在,我們分別為每個區域定義資源。控制器名稱是資源,其操作是對資源的訪問: ```php <?php use Phalcon\Acl\Resource; // ... // Private area resources (backend) $privateResources = [ 'companies' => ['index', 'search', 'new', 'edit', 'save', 'create', 'delete'], 'products' => ['index', 'search', 'new', 'edit', 'save', 'create', 'delete'], 'producttypes' => ['index', 'search', 'new', 'edit', 'save', 'create', 'delete'], 'invoices' => ['index', 'profile'], ]; foreach ($privateResources as $resourceName => $actions) { $acl->addResource( new Resource($resourceName), $actions ); } // Public area resources (frontend) $publicResources = [ 'index' => ['index'], 'about' => ['index'], 'register' => ['index'], 'errors' => ['show404', 'show500'], 'session' => ['index', 'register', 'start', 'end'], 'contact' => ['index', 'send'], ]; foreach ($publicResources as $resourceName => $actions) { $acl->addResource( new Resource($resourceName), $actions ); } ``` ACL現在知道現有控制器及其相關操作。角色`Users`可以訪問前端和后端的所有資源。角色`Guests` 只能訪問公共區域: ```php <?php // Grant access to public areas to both users and guests foreach ($roles as $role) { foreach ($publicResources as $resource => $actions) { $acl->allow( $role->getName(), $resource, '*' ); } } // Grant access to private area only to role Users foreach ($privateResources as $resource => $actions) { foreach ($actions as $action) { $acl->allow( 'Users', $resource, $action ); } } ``` ## 使用 CRUD 后端通常提供允許用戶操作數據的表單。繼續對INVO的解釋,我們現在討論CRUD的創建,這是Phalcon將使用表單,驗證,分頁器等方式為您提供的一項非常常見的任務。 大多數操縱INVO中數據的選項(公司,產品和產品類型)都是使用基本和通用 [CRUD](http://en.wikipedia.org/wiki/Create,_read,_update_and_delete) (創建,讀取,更新和刪除)開發的。每個CRUD包含以下文件: ```bash invo/ app/ controllers/ ProductsController.php models/ Products.php forms/ ProductsForm.php views/ products/ edit.volt index.volt new.volt search.volt ``` 每個控制器都有以下操作: ```php <?php class ProductsController extends ControllerBase { /** * The start action, it shows the 'search' view */ public function indexAction() { // ... } /** * Execute the 'search' based on the criteria sent from the 'index' * Returning a paginator for the results */ public function searchAction() { // ... } /** * Shows the view to create a 'new' product */ public function newAction() { // ... } /** * Shows the view to 'edit' an existing product */ public function editAction() { // ... } /** * Creates a product based on the data entered in the 'new' action */ public function createAction() { // ... } /** * Updates a product based on the data entered in the 'edit' action */ public function saveAction() { // ... } /** * Deletes an existing product */ public function deleteAction($id) { // ... } } ``` ## 搜索表單 每個CRUD都以搜索表單開頭。此表單顯示表具有的每個字段(產品),允許用戶為任何字段創建搜索條件。`products`表與表`products_types`有關系。在這種情況下,我們先前查詢了此表中的記錄,以便于按該字段進行搜索: ```php <?php /** * The start action, it shows the 'search' view */ public function indexAction() { $this->persistent->searchParams = null; $this->view->form = new ProductsForm(); } ``` `ProductsForm`表單的一個實例(`app/forms/ProductsForm.php`)被傳遞給視圖。此表單定義了用戶可見的字段: ```php <?php use Phalcon\Forms\Form; use Phalcon\Forms\Element\Text; use Phalcon\Forms\Element\Hidden; use Phalcon\Forms\Element\Select; use Phalcon\Validation\Validator\Email; use Phalcon\Validation\Validator\PresenceOf; use Phalcon\Validation\Validator\Numericality; class ProductsForm extends Form { /** * Initialize the products form */ public function initialize($entity = null, $options = []) { if (!isset($options['edit'])) { $element = new Text('id'); $element->setLabel('Id'); $this->add($element); } else { $this->add(new Hidden('id')); } $name = new Text('name'); $name->setLabel('Name'); $name->setFilters( [ 'striptags', 'string', ] ); $name->addValidators( [ new PresenceOf( [ 'message' => 'Name is required', ] ) ] ); $this->add($name); $type = new Select( 'profilesId', ProductTypes::find(), [ 'using' => [ 'id', 'name', ], 'useEmpty' => true, 'emptyText' => '...', 'emptyValue' => '', ] ); $this->add($type); $price = new Text('price'); $price->setLabel('Price'); $price->setFilters( [ 'float', ] ); $price->addValidators( [ new PresenceOf( [ 'message' => 'Price is required', ] ), new Numericality( [ 'message' => 'Price is required', ] ), ] ); $this->add($price); } } ``` 使用基于表單組件提供的元素的面向對象方案聲明表單。每個元素都遵循幾乎相同的結構: ```php <?php // Create the element $name = new Text('name'); // Set its label $name->setLabel('Name'); // Before validating the element apply these filters $name->setFilters( [ 'striptags', 'string', ] ); // Apply this validators $name->addValidators( [ new PresenceOf( [ 'message' => 'Name is required', ] ) ] ); // Add the element to the form $this->add($name); ``` 其他元素也以這種形式使用: ```php <?php // Add a hidden input to the form $this->add( new Hidden('id') ); // ... $productTypes = ProductTypes::find(); // Add a HTML Select (list) to the form // and fill it with data from 'product_types' $type = new Select( 'profilesId', $productTypes, [ 'using' => [ 'id', 'name', ], 'useEmpty' => true, 'emptyText' => '...', 'emptyValue' => '', ] ); ``` 請注意,`ProductTypes::find()`包含使用`Phalcon\Tag::select()`填充SELECT標記所需的數據。將表單傳遞給視圖后,可以將其呈現并呈現給用戶: ```twig {{ form('products/search') }} <h2> Search products </h2> <fieldset> {% for element in form %} <div class='control-group'> {{ element.label(['class': 'control-label']) }} <div class='controls'> {{ element }} </div> </div> {% endfor %} <div class='control-group'> {{ submit_button('Search', 'class': 'btn btn-primary') }} </div> </fieldset> {{ endForm() }} ``` 這會產生以下HTML: ```html <form action='/invo/products/search' method='post'> <h2> Search products </h2> <fieldset> <div class='control-group'> <label for='id' class='control-label'>Id</label> <div class='controls'> <input type='text' id='id' name='id' /> </div> </div> <div class='control-group'> <label for='name' class='control-label'>Name</label> <div class='controls'> <input type='text' id='name' name='name' /> </div> </div> <div class='control-group'> <label for='profilesId' class='control-label'>profilesId</label> <div class='controls'> <select id='profilesId' name='profilesId'> <option value=''>...</option> <option value='1'>Vegetables</option> <option value='2'>Fruits</option> </select> </div> </div> <div class='control-group'> <label for='price' class='control-label'>Price</label> <div class='controls'> <input type='text' id='price' name='price' /> </div> </div> <div class='control-group'> <input type='submit' value='Search' class='btn btn-primary' /> </div> </fieldset> </form> ``` 當提交表單時,在控制器中執行`search`動作,該控制器基于用戶輸入的數據執行搜索。 ## 執行搜索 `search` 操作有兩種行為。當通過POST訪問時,它會根據表單發送的數據執行搜索,但是當通過GET訪問時,它會移動分頁器中的當前頁面。為區分HTTP方法,我們使用`Request`組件進行檢查: ```php <?php /** * Execute the 'search' based on the criteria sent from the 'index' * Returning a paginator for the results */ public function searchAction() { if ($this->request->isPost()) { // Create the query conditions } else { // Paginate using the existing conditions } // ... } ``` 在 `Phalcon\Mvc\Model\Criteria`的幫助下,我們可以根據表單發送的數據類型和值智能地創建搜索條件: ```php <?php $query = Criteria::fromInput( $this->di, 'Products', $this->request->getPost() ); ``` 此方法驗證哪些值與''(空字符串)和null不同,并將它們考慮在內以創建搜索條件: * 如果字段數據類型是文本或類似(char,varchar,text等),它使用SQL的`like`運算符來過濾結果。 * 如果數據類型不是文本或類似的,它將使用 `=` 運算符。 此外,`Criteria`忽略所有與表中的任何字段都不匹配的`$_POST`變量。使用`綁定參數`自動轉義值。 現在,我們將生成的參數存儲在控制器的會話包中: ```php <?php $this->persistent->searchParams = $query->getParams(); ``` 會話包是控制器中的一個特殊屬性,它在使用會話服務的請求之間保持不變。訪問時,此屬性會在每個控制器中注入一個獨立的`Phalcon\Session\Bag`實例。 然后,基于構建的參數,我們執行查詢: ```php <?php $products = Products::find($parameters); if (count($products) === 0) { $this->flash->notice( 'The search did not found any products' ); return $this->dispatcher->forward( [ 'controller' => 'products', 'action' => 'index', ] ); } ``` 如果搜索未返回任何產品,我們會再次將用戶轉發給索引操作。讓我們假裝搜索返回結果,然后我們創建一個分頁器來輕松瀏覽它們: ```php <?php use Phalcon\Paginator\Adapter\Model as Paginator; // ... $paginator = new Paginator( [ 'data' => $products, // Data to paginate 'limit' => 5, // Rows per page 'page' => $numberPage, // Active page ] ); // Get active page in the paginator $page = $paginator->getPaginate(); ``` 最后我們傳遞返回的頁面到視圖: ```php <?php $this->view->page = $page; ``` 在視圖(`app/views/products/search.volt`)中,我們遍歷與當前頁面對應的結果,向用戶顯示當前頁面中的每一行: ```twig {% for product in page.items %} {% if loop.first %} <table> <thead> <tr> <th>Id</th> <th>Product Type</th> <th>Name</th> <th>Price</th> <th>Active</th> </tr> </thead> <tbody> {% endif %} <tr> <td> {{ product.id }} </td> <td> {{ product.getProductTypes().name }} </td> <td> {{ product.name }} </td> <td> {{ '%.2f'|format(product.price) }} </td> <td> {{ product.getActiveDetail() }} </td> <td width='7%'> {{ link_to('products/edit/' ~ product.id, 'Edit') }} </td> <td width='7%'> {{ link_to('products/delete/' ~ product.id, 'Delete') }} </td> </tr> {% if loop.last %} </tbody> <tbody> <tr> <td colspan='7'> <div> {{ link_to('products/search', 'First') }} {{ link_to('products/search?page=' ~ page.before, 'Previous') }} {{ link_to('products/search?page=' ~ page.next, 'Next') }} {{ link_to('products/search?page=' ~ page.last, 'Last') }} <span class='help-inline'>{{ page.current }} of {{ page.total_pages }}</span> </div> </td> </tr> </tbody> </table> {% endif %} {% else %} No products are recorded {% endfor %} ``` 上面的例子中有很多東西值得詳細說明。首先,使用Volt `for`來遍歷當前頁面中的活動項目。Volt為PHP `foreach`提供了更簡單的語法。 ```twig {% for product in page.items %} ``` 與在PHP中的以下內容相同: ```php <?php foreach ($page->items as $product) { ?> ``` 整個`for` 塊提供以下內容: ```twig {% for product in page.items %} {% if loop.first %} Executed before the first product in the loop {% endif %} Executed for every product of page.items {% if loop.last %} Executed after the last product is loop {% endif %} {% else %} Executed if page.items does not have any products {% endfor %} ``` 現在,您可以返回視圖并查看每個塊正在執行的操作。`product`中的每個字段都相應打印: ```twig <tr> <td> {{ product.id }} </td> <td> {{ product.productTypes.name }} </td> <td> {{ product.name }} </td> <td> {{ '%.2f'|format(product.price) }} </td> <td> {{ product.getActiveDetail() }} </td> <td width='7%'> {{ link_to('products/edit/' ~ product.id, 'Edit') }} </td> <td width='7%'> {{ link_to('products/delete/' ~ product.id, 'Delete') }} </td> </tr> ``` 正如我們之前看到的那樣,使用`product.id`與PHP中的相同:`$product->id`,我們使用`product.name`進行相同的操作。其他字段的呈現方式不同,例如,讓我們將焦點放在`product.productTypes.name`中。要理解這一部分,我們必須查看Product模型(`app/models/Products.php`): ```php <?php use Phalcon\Mvc\Model; /** * Products */ class Products extends Model { // ... /** * Products initializer */ public function initialize() { $this->belongsTo( 'product_types_id', 'ProductTypes', 'id', [ 'reusable' => true, ] ); } // ... } ``` 模型可以有一個名為`initialize()`的方法,每個請求調用一次該方法,并為ORM初始化模型。在這種情況下,通過定義此模型與另一個名為“ProductTypes”的模型具有一對多關系來初始化“Products”。 ```php <?php $this->belongsTo( 'product_types_id', 'ProductTypes', 'id', [ 'reusable' => true, ] ); ``` 這意味著,`Products`中的本地屬性`product_types_id`在其屬性`id`中與`ProductTypes`模型具有一對多的關系。通過定義此關系,我們可以使用以下方法訪問產品類型的名稱: ```twig <td>{{ product.productTypes.name }}</td> ``` 使用Volt過濾器格式化字段`price` : ```twig <td>{{ '%.2f'|format(product.price) }}</td> ``` 在普通的PHP中,這將是: ```php <?php echo sprintf('%.2f', $product->price) ?> ``` 打印產品是否處于活動狀態使用模型中實現的幫助程序: ```php <td>{{ product.getActiveDetail() }}</td> ``` 此方法在模型中定義。 ## 創建和更新記錄 現在讓我們看看CRUD如何創建和更新記錄。在`new`視圖和`edit`視圖中,用戶輸入的數據將分別發送到執行創建和更新產品操作的`create`和`save` 操作。 在創建案例中,我們恢復提交的數據并將其分配給新的`Products`實例: ```php <?php /** * Creates a product based on the data entered in the 'new' action */ public function createAction() { if (!$this->request->isPost()) { return $this->dispatcher->forward( [ 'controller' => 'products', 'action' => 'index', ] ); } $form = new ProductsForm(); $product = new Products(); $product->id = $this->request->getPost('id', 'int'); $product->product_types_id = $this->request->getPost('product_types_id', 'int'); $product->name = $this->request->getPost('name', 'striptags'); $product->price = $this->request->getPost('price', 'double'); $product->active = $this->request->getPost('active'); // ... } ``` 還記得我們在產品表單中定義的過濾器嗎?在分配給對象`$product`之前過濾數據。這種過濾是可選的;ORM還會轉義輸入數據并根據列類型執行其他轉換: ```php <?php // ... $name = new Text('name'); $name->setLabel('Name'); // Filters for name $name->setFilters( [ 'striptags', 'string', ] ); // Validators for name $name->addValidators( [ new PresenceOf( [ 'message' => 'Name is required', ] ) ] ); $this->add($name); ``` 保存時,我們將知道數據是否符合以`ProductsForm`形式(`app/forms/ProductsForm.php`)形式實現的業務規則和驗證: ```php <?php // ... $form = new ProductsForm(); $product = new Products(); // Validate the input $data = $this->request->getPost(); if (!$form->isValid($data, $product)) { $messages = $form->getMessages(); foreach ($messages as $message) { $this->flash->error($message); } return $this->dispatcher->forward( [ 'controller' => 'products', 'action' => 'new', ] ); } ``` 最后,如果表單沒有返回任何驗證消息,我們可以保存產品實例: ```php <?php // ... if ($product->save() === false) { $messages = $product->getMessages(); foreach ($messages as $message) { $this->flash->error($message); } return $this->dispatcher->forward( [ 'controller' => 'products', 'action' => 'new', ] ); } $form->clear(); $this->flash->success( 'Product was created successfully' ); return $this->dispatcher->forward( [ 'controller' => 'products', 'action' => 'index', ] ); ``` 現在,在更新產品的情況下,我們必須首先向用戶顯示當前在編輯記錄中的數據: ```php <?php /** * Edits a product based on its id */ public function editAction($id) { if (!$this->request->isPost()) { $product = Products::findFirstById($id); if (!$product) { $this->flash->error( 'Product was not found' ); return $this->dispatcher->forward( [ 'controller' => 'products', 'action' => 'index', ] ); } $this->view->form = new ProductsForm( $product, [ 'edit' => true, ] ); } } ``` 通過將模型作為第一個參數傳遞,找到的數據綁定到表單。由于這個原因,用戶可以更改任何值,然后通過 `save` 操作將其發送回數據庫: ```php <?php /** * Updates a product based on the data entered in the 'edit' action */ public function saveAction() { if (!$this->request->isPost()) { return $this->dispatcher->forward( [ 'controller' => 'products', 'action' => 'index', ] ); } $id = $this->request->getPost('id', 'int'); $product = Products::findFirstById($id); if (!$product) { $this->flash->error( 'Product does not exist' ); return $this->dispatcher->forward( [ 'controller' => 'products', 'action' => 'index', ] ); } $form = new ProductsForm(); $data = $this->request->getPost(); if (!$form->isValid($data, $product)) { $messages = $form->getMessages(); foreach ($messages as $message) { $this->flash->error($message); } return $this->dispatcher->forward( [ 'controller' => 'products', 'action' => 'new', ] ); } if ($product->save() === false) { $messages = $product->getMessages(); foreach ($messages as $message) { $this->flash->error($message); } return $this->dispatcher->forward( [ 'controller' => 'products', 'action' => 'new', ] ); } $form->clear(); $this->flash->success( 'Product was updated successfully' ); return $this->dispatcher->forward( [ 'controller' => 'products', 'action' => 'index', ] ); } ``` ## 用戶組件 應用程序的所有UI元素和視覺樣式主要通過[Bootstrap](http://getbootstrap.com/)實現。某些元素(例如導航欄)會根據應用程序的狀態而更改。例如,在右上角,如果用戶登錄到應用程序,則`Log in / Sign Up`鏈接將更改為`Log out`。 應用程序的這一部分在組件`Elements`(`app/library/Elements.php`)中實現。 ```php <?php use Phalcon\Mvc\User\Component; class Elements extends Component { public function getMenu() { // ... } public function getTabs() { // ... } } ``` 此類擴展了`Phalcon\Mvc\User\Component`。它不是用于擴展具有此類的組件,但它有助于更??快地訪問應用程序服務。現在,我們將在服務容器中注冊我們的第一個用戶組件: ```php <?php // Register a user component $di->set( 'elements', function () { return new Elements(); } ); ``` 作為視圖中的控制器,插件或組件,該組件還可以訪問容器中注冊的服務,只需訪問與以前注冊的服務同名的屬性: ```twig <div class='navbar navbar-fixed-top'> <div class='navbar-inner'> <div class='container'> <a class='btn btn-navbar' data-toggle='collapse' data-target='.nav-collapse'> <span class='icon-bar'></span> <span class='icon-bar'></span> <span class='icon-bar'></span> </a> <a class='brand' href='#'>INVO</a> {{ elements.getMenu() }} </div> </div> </div> <div class='container'> {{ content() }} <hr> <footer> <p>&copy; Company 2017</p> </footer> </div> ``` 重要的是: ```twig {{ elements.getMenu() }} ``` ## 動態更改標題 當您在一個選項和另一個選項之間瀏覽時,會看到標題會動態更改,指示我們當前的工作位置。這是在每個控制器初始化程序中實現的: ```php <?php class ProductsController extends ControllerBase { public function initialize() { // Set the document title $this->tag->setTitle( 'Manage your product types' ); parent::initialize(); } // ... } ``` 注意,也調用方法`parent::initialize()` ,它向標題添加更多數據: ```php <?php use Phalcon\Mvc\Controller; class ControllerBase extends Controller { protected function initialize() { // Prepend the application name to the title $this->tag->prependTitle('INVO | '); } // ... } ``` 最后,標題打印在主視圖中(app/views/index.volt): ```php <!DOCTYPE html> <html> <head> <?php echo $this->tag->getTitle(); ?> </head> <!-- ... --> </html> ```
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看