• No se han encontrado resultados

CAPÍTULO 4: VALIDACIÓN DE LA SOLUCIÓN

4.2 V ALIDACIÓN M ÓDULO P RESENTACIÓN

respec-tively. Anything in between will be rendered as is.

<?php

use yii\widgets\ActiveForm;

use yii\helpers\Html;

?>

<?php $form = ActiveForm::begin(['id' => 'login-form']); ?>

<?= $form->field($model, 'username') ?>

<?= $form->field($model, 'password')->passwordInput() ?>

<div class="form-group">

<?= Html::submitButton('Login') ?>

</div>

<?php ActiveForm::end(); ?>

Note that unlike yii\base\Widget::widget() which returns the rendering result of a widget, the method yii\base\Widget::begin() returns an in-stance of the widget which you can use to build the widget content.

3.10.2 Creating Widgets

To create a widget, extend from yii\base\Widget and override the yii

\base\Widget::init() and/or yii\base\Widget::run() methods. Usu-ally, the init() method should contain the code that normalizes the widget properties, while the run() method should contain the code that generates the rendering result of the widget. The rendering result may be directly

echoed or returned as a string by run().

In the following example, HelloWidget HTML-encodes and displays the content assigned to its message property. If the property is not set, it will display Hello World by default.

namespace app\components;

use yii\base\Widget;

use yii\helpers\Html;

class HelloWidget extends Widget {

public $message;

public function init() { parent::init();

if ($this->message === null) {

$this->message = 'Hello World';

} }

114 CHAPTER 3. APPLICATION STRUCTURE

public function run()

{ return Html::encode($this->message);

} }

To use this widget, simply insert the following code in a view:

<?php

use app\components\HelloWidget;

?>

<?= HelloWidget::widget(['message' => 'Good morning']) ?>

Below is a variant ofHelloWidgetwhich takes the content enclosed within the

begin()and end() calls, HTML-encodes it and then displays it.

namespace app\components;

use yii\base\Widget;

use yii\helpers\Html;

class HelloWidget extends Widget {

public function init() {

parent::init();

ob_start();

}

public function run()

{ $content = ob_get_clean();

return Html::encode($content);

} }

As you can see, PHP's output buer is started ininit() so that any output between the calls ofinit()andrun()can be captured, processed and returned inrun().

Info: When you call yii\base\Widget::begin(), a new instance of the widget will be created and theinit()method will be called at the end of the widget constructor. When you call yii\base

\Widget::end(), the run() method will be called whose return result will be echoed byend().

The following code shows how to use this new variant ofHelloWidget:

<?php

use app\components\HelloWidget;

?>

<?php HelloWidget::begin(); ?>

3.11. ASSETS 115

content that may contain <tag>'s

<?php HelloWidget::end(); ?>

Sometimes, a widget may need to render a big chunk of content. While you can embed the content within therun() method, a better approach is to put it in a view and call yii\base\Widget::render() to render it. For example,

public function run()

{ return $this->render('hello');

}

By default, views for a widget should be stored in les in the WidgetPath /views directory, where WidgetPath stands for the directory containing the widget class le. Therefore, the above example will render the view le

@app/components/views/hello.php, assuming the widget class is located under

@app/components. You may override the yii\base\Widget::getViewPath() method to customize the directory containing the widget view les.

3.10.3 Best Practices

Widgets are an object-oriented way of reusing view code.

When creating widgets, you should still follow the MVC pattern. In general, you should keep logic in widget classes and keep presentation in views.

Widgets should be designed to be self-contained. That is, when using a widget, you should be able to just drop it in a view without doing anything else. This could be tricky if a widget requires external resources, such as CSS, JavaScript, images, etc. Fortunately, Yii provides the support for asset bundles, which can be utilized to solve the problem.

When a widget contains view code only, it is very similar to a view. In fact, in this case, their only dierence is that a widget is a redistributable class, while a view is just a plain PHP script that you would prefer to keep within your application.

3.11 Assets

An asset in Yii is a le that may be referenced in a Web page. It can be a CSS le, a JavaScript le, an image or video le, etc. Assets are located in Web-accessible directories and are directly served by Web servers.

It is often preferable to manage assets programmatically. For example, when you use the yii\jui\DatePicker widget in a page, it will automat-ically include the required CSS and JavaScript les, instead of asking you to manually nd these les and include them. And when you upgrade the widget to a new version, it will automatically use the new version of the

116 CHAPTER 3. APPLICATION STRUCTURE asset les. In this tutorial, we will describe the powerful asset management capability provided in Yii.

3.11.1 Asset Bundles

Yii manages assets in the unit of asset bundle. An asset bundle is simply a collection of assets located in a directory. When you register an asset bundle in a view, it will include the CSS and JavaScript les in the bundle in the rendered Web page.

3.11.2 Dening Asset Bundles

Asset bundles are specied as PHP classes extending from yii\web\AssetBundle.

The name of a bundle is simply its corresponding fully qualied PHP class name (without the leading backslash). An asset bundle class should be au-toloadable. It usually species where the assets are located, what CSS and JavaScript les the bundle contains, and how the bundle depends on other bundles.

The following code denes the main asset bundle used by the basic project template:

<?php

namespace app\assets;

use yii\web\AssetBundle;

class AppAsset extends AssetBundle {

public $basePath = '@webroot';

public $baseUrl = '@web';

public $css = [ 'css/site.css', ];public $js = [ ];

public $depends = [ 'yii\web\YiiAsset',

'yii\bootstrap\BootstrapAsset', ];

}

The aboveAppAsset class species that the asset les are located under the

@webrootdirectory which corresponds to the URL@web; the bundle contains a single CSS lecss/site.cssand no JavaScript le; the bundle depends on two other bundles: yii\web\YiiAsset and yii\bootstrap\BootstrapAsset.

More detailed explanation about the properties of yii\web\AssetBundle can be found in the following:

3.11. ASSETS 117

• sourcePath: species the root directory that contains the asset les in this bundle. This property should be set if the root directory is not Web accessible. Otherwise, you should set the basePath property and baseUrl, instead. Path aliases can be used here.

• basePath: species a Web-accessible directory that contains the asset

les in this bundle. When you specify the sourcePath property, the asset manager will publish the assets in this bundle to a Web-accessible directory and overwrite this property accordingly. You should set this property if your asset les are already in a Web-accessible directory and do not need asset publishing. Path aliases can be used here.

• baseUrl: species the URL corresponding to the directory basePath.

Like basePath, if you specify the sourcePath property, the asset man-ager will publish the assets and overwrite this property accordingly.

Path aliases can be used here.

• js: an array listing the JavaScript les contained in this bundle. Note that only forward slash / should be used as directory separators. Each JavaScript le can be specied in one of the following two formats:

 a relative path representing a local JavaScript le (e.g. js/main.

js). The actual path of the le can be determined by prepending yii\web\AssetManager::$basePath to the relative path, and the actual URL of the le can be determined by prepending yii\web

\AssetManager::$baseUrl to the relative path.

 an absolute URL representing an external JavaScript le. For ex-ample,http://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min .jsor //ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js.

• css: an array listing the CSS les contained in this bundle. The format of this array is the same as that of js.

• depends: an array listing the names of the asset bundles that this bundle depends on (to be explained shortly).

• jsOptions: species the options that will be passed to the yii\web

\View::registerJsFile() method when it is called to register every JavaScript le in this bundle.

• cssOptions: species the options that will be passed to the yii\web

\View::registerCssFile() method when it is called to register every CSS le in this bundle.

• publishOptions: species the options that will be passed to the yii

\web\AssetManager::publish() method when it is called to publish source asset les to a Web directory. This is only used if you specify the sourcePath property.

Asset Locations

Assets, based on their location, can be classied as:

• source assets: the asset les are located together with PHP source code

118 CHAPTER 3. APPLICATION STRUCTURE which cannot be directly accessed via Web. In order to use source assets in a page, they should be copied to a Web directory and turned into the so-called published assets. This process is called asset publishing which will be described in detail shortly.

• published assets: the asset les are located in a Web directory and can thus be directly accessed via Web.

• external assets: the asset les are located on a Web server that is dierent from the one hosting your Web application.

When dening an asset bundle class, if you specify the sourcePath property, it means any assets listed using relative paths will be considered as source assets. If you do not specify this property, it means those assets are published assets (you should therefore specify basePath and baseUrl to let Yii know where they are located).

It is recommended that you place assets belonging to an application in a Web directory to avoid the unnecessary asset publishing process. This is whyAppAssetin the prior example species basePath instead of sourcePath.

For extensions, because their assets are located together with their source code in directories that are not Web accessible, you have to specify the sourcePath property when dening asset bundle classes for them.

Note: Do not use@webroot/assets as the source path. This di-rectory is used by default by the asset manager to save the asset

les published from their source location. Any content in this di-rectory is considered temporarily and may be subject to removal.

Asset Dependencies

When you include multiple CSS or JavaScript les in a Web page, they have to follow a certain order to avoid overriding issues. For example, if you are using a jQuery UI widget in a Web page, you have to make sure the jQuery JavaScript le is included before the jQuery UI JavaScript le. We call such ordering the dependencies among assets.

Asset dependencies are mainly specied through the yii\web\AssetBundle ::$depends property. In theAppAsset example, the asset bundle depends on two other asset bundles: yii\web\YiiAsset and yii\bootstrap\BootstrapAsset, which means the CSS and JavaScript les inAppAsset will be included after those les in the two dependent bundles.

Asset dependencies are transitive. This means if bundle A depends on B which depends on C, A will depend on C, too.

Asset Options

You can specify the cssOptions and jsOptions properties to customize the way that CSS and JavaScript les are included in a page. The values of these properties will be passed to the yii\web\View::registerCssFile()

3.11. ASSETS 119 and yii\web\View::registerJsFile() methods, respectively, when they are called by the view to include CSS and JavaScript les.

Note: The options you set in a bundle class apply to every CSS/-JavaScript le in the bundle. If you want to use dierent options for dierent les, you should create separate asset bundles, and use one set of options in each bundle.

For example, to conditionally include a CSS le for browsers that are IE9 or below, you can use the following option:

public $cssOptions = ['condition' => 'lte IE9'];

This will cause a CSS le in the bundle to be included using the following HTML tags:

<!--[if lte IE9]>

<link rel="stylesheet" href="path/to/foo.css">

<![endif]-->

To wrap the generated CSS link tags within <noscript>, you can congure

cssOptionsas follows,

public $cssOptions = ['noscript' => true];

To include a JavaScript le in the head section of a page (by default, JavaScript

les are included at the end of the body section), use the following option:

public $jsOptions = ['position' => \yii\web\View::POS_HEAD];

By default, when an asset bundle is being published, all contents in the di-rectory specied by yii\web\AssetBundle::$sourcePath will be published.

You can customize this behavior by conguring the publishOptions prop-erty. For example, to publish only one or a few subdirectories of yii\web

\AssetBundle::$sourcePath, you can do the following in the asset bundle class:

<?php

namespace app\assets;

use yii\web\AssetBundle;

class FontAwesomeAsset extends AssetBundle { public $sourcePath = '@bower/font-awesome';

public $css = [

'css/font-awesome.min.css', ];

public function init() { parent::init();

$this->publishOptions['beforeCopy'] = function ($from, $to) {

$dirname = basename(dirname($from));

120 CHAPTER 3. APPLICATION STRUCTURE

return $dirname === 'fonts' || $dirname === 'css';

} };

}

The above example denes an asset bundle for the fontawesome package21. By specifying thebeforeCopy publishing option, only the fonts and css sub-directories will be published.

Bower and NPM Assets

Most JavaScript/CSS packages are managed by Bower22 and/or NPM23. If your application or extension is using such a package, it is recommended that you follow these steps to manage the assets in the library:

1. Modify the composer.jsonle of your application or extension and list the package in therequireentry. You should usebower-asset/PackageName

(for Bower packages) or npm-asset/PackageName (for NPM packages) to refer to the library.

2. Create an asset bundle class and list the JavaScript/CSS les that you plan to use in your application or extension. You should specify the sourcePath property as @bower/PackageName or @npm/PackageName. This is because Composer will install the Bower or NPM package in the directory corresponding to this alias.

Note: Some packages may put all their distributed les in a sub-directory. If this is the case, you should specify the subdirectory as the value of sourcePath. For example, yii\web\JqueryAsset uses@bower/jquery/dist instead of@bower/jquery.

3.11.3 Using Asset Bundles

To use an asset bundle, register it with a view by calling the yii\web

\AssetBundle::register() method. For example, in a view template you can register an asset bundle like the following:

use app\assets\AppAsset;

AppAsset::register($this); // $this represents the view object

Info: The yii\web\AssetBundle::register() method returns an asset bundle object containing the information about the pub-lished assets, such as basePath or baseUrl.

21http://fontawesome.io/

22http://bower.io/

23https://www.npmjs.org/

3.11. ASSETS 121