Browse Source
* http://github.com/yiisoft/yii2: (23 commits) move JsExpression to web. Fixes issue #155. Fixes the variable name Added twig, smarty and php-markdown dependencies, vendor is gitignored now added composer.json fix fix Wrapped code with github-style blocks with PHP highlighting Fixed typo Controllers section example fix Renamed YiiBase to \yii\YiiBase. Added namespace section. Added dirty attribute description. Fixed doc about renderers. Finished the initial draft of upgrading instructions Fix attaching behavior via config Changed default value of View::renderers. Fix attaching behavior via config Menu WIP upgrading instructions WIP ...tags/2.0.0-beta
sensorario
12 years ago
26 changed files with 1053 additions and 72 deletions
@ -0,0 +1,75 @@ |
|||||||
|
{ |
||||||
|
"name": "yiisoft/yii2", |
||||||
|
"description": "Yii2 Web Programming Framework", |
||||||
|
"keywords": ["yii", "framework"], |
||||||
|
"homepage": "http://www.yiiframework.com/", |
||||||
|
"type": "library", |
||||||
|
"license": "BSD-3-Clause", |
||||||
|
"authors": [ |
||||||
|
{ |
||||||
|
"name": "Qiang Xue", |
||||||
|
"email": "qiang.xue@gmail.com", |
||||||
|
"homepage": "http://www.yiiframework.com/", |
||||||
|
"role": "Founder and project lead" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "Alexander Makarov", |
||||||
|
"email": "sam@rmcreative.ru", |
||||||
|
"homepage": "http://rmcreative.ru/", |
||||||
|
"role": "Core framework development" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "Maurizio Domba", |
||||||
|
"homepage": "http://mdomba.info/", |
||||||
|
"role": "Core framework development" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "Carsten Brandt", |
||||||
|
"email": "mail@cebe.cc", |
||||||
|
"homepage": "http://cebe.cc/", |
||||||
|
"role": "Core framework development" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "Timur Ruziev", |
||||||
|
"email": "resurtm@gmail.com", |
||||||
|
"homepage": "http://resurtm.com/", |
||||||
|
"role": "Core framework development" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "Paul Klimov", |
||||||
|
"email": "klimov.paul@gmail.com", |
||||||
|
"role": "Core framework development" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "Wei Zhuo", |
||||||
|
"email": "weizhuo@gmail.com", |
||||||
|
"role": "Project site maintenance and development" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "Sebastián Thierer", |
||||||
|
"email": "sebas@artfos.com", |
||||||
|
"role": "Component development" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "Jeffrey Winesett", |
||||||
|
"email": "jefftulsa@gmail.com", |
||||||
|
"role": "Documentation and marketing" |
||||||
|
} |
||||||
|
], |
||||||
|
"support": { |
||||||
|
"issues": "https://github.com/yiisoft/yii2/issues?state=open", |
||||||
|
"forum": "http://www.yiiframework.com/forum/", |
||||||
|
"wiki": "http://www.yiiframework.com/wiki/", |
||||||
|
"irc": "irc://irc.freenode.net/yii", |
||||||
|
"source": "https://github.com/yiisoft/yii2" |
||||||
|
}, |
||||||
|
"bin": [ |
||||||
|
"framework/yiic" |
||||||
|
], |
||||||
|
"require": { |
||||||
|
"php": ">=5.3.0", |
||||||
|
"michelf/php-markdown": "1.3", |
||||||
|
"twig/twig": "1.12.*", |
||||||
|
"smarty/smarty": "3.1.*" |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,459 @@ |
|||||||
|
Upgrading from Yii 1.1 |
||||||
|
====================== |
||||||
|
|
||||||
|
In this chapter, we list the major changes introduced in Yii 2.0 since version 1.1. |
||||||
|
We hope this list will make it easier for you to upgrade from Yii 1.1 and quickly |
||||||
|
master Yii 2.0 based on your existing Yii knowledge. |
||||||
|
|
||||||
|
|
||||||
|
Namespace |
||||||
|
--------- |
||||||
|
|
||||||
|
The most obvious change in Yii 2.0 is the use of namespaces. Almost every core class |
||||||
|
is namespaced, e.g., `yii\web\Request`. The "C" prefix is no longer used in class names. |
||||||
|
The naming of the namespaces follows the directory structure. For example, `yii\web\Request` |
||||||
|
indicates the corresponding class file is `web/Request.php` under the Yii framework folder. |
||||||
|
You can use any core class without explicitly include that class file, thanks to the Yii |
||||||
|
class loader. |
||||||
|
|
||||||
|
|
||||||
|
Component and Object |
||||||
|
-------------------- |
||||||
|
|
||||||
|
Yii 2.0 breaks the `CComponent` class in 1.1 into two classes: `Object` and `Component`. |
||||||
|
The `Object` class is a lightweight base class that allows defining class properties |
||||||
|
via getters and setters. The `Component` class extends from `Object` and supports |
||||||
|
the event feature and the behavior feature. |
||||||
|
|
||||||
|
If your class does not need the event or behavior feature, you should consider using |
||||||
|
`Object` as the base class. This is usually the case for classes that represent basic |
||||||
|
data structures. |
||||||
|
|
||||||
|
|
||||||
|
Object Configuration |
||||||
|
-------------------- |
||||||
|
|
||||||
|
The `Object` class introduces a uniform way of configuring objects. Any descendant class |
||||||
|
of `Object` should declare its constructor (if needed) in the following way so that |
||||||
|
it can be properly configured: |
||||||
|
|
||||||
|
```php |
||||||
|
class MyClass extends \yii\Object |
||||||
|
{ |
||||||
|
public function __construct($param1, $param2, $config = array()) |
||||||
|
{ |
||||||
|
// ... initialization before configuration is applied |
||||||
|
|
||||||
|
parent::__construct($config); |
||||||
|
} |
||||||
|
|
||||||
|
public function init() |
||||||
|
{ |
||||||
|
parent::init(); |
||||||
|
|
||||||
|
// ... initialization after configuration is applied |
||||||
|
} |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
In the above, the last parameter of the constructor must take a configuration array |
||||||
|
which contains name-value pairs for initializing the properties at the end of the constructor. |
||||||
|
You can override the `init()` method to do initialization work that should be done after |
||||||
|
the configuration is applied. |
||||||
|
|
||||||
|
By following this convention, you will be able to create and configure a new object |
||||||
|
using a configuration array like the following: |
||||||
|
|
||||||
|
```php |
||||||
|
$object = Yii::createObject(array( |
||||||
|
'class' => 'MyClass', |
||||||
|
'property1' => 'abc', |
||||||
|
'property2' => 'cde', |
||||||
|
), $param1, $param2); |
||||||
|
``` |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Events |
||||||
|
------ |
||||||
|
|
||||||
|
There is no longer the need to define an `on`-method in order to define an event in Yii 2.0. |
||||||
|
Instead, you can use whatever event names. To attach a handler to an event, you should |
||||||
|
use the `on` method now: |
||||||
|
|
||||||
|
```php |
||||||
|
$component->on($eventName, $handler); |
||||||
|
// To detach the handler, use: |
||||||
|
// $component->off($eventName, $handler); |
||||||
|
``` |
||||||
|
|
||||||
|
|
||||||
|
When you attach a handler, you can now associate it with some parameters which can be later |
||||||
|
accessed via the event parameter by the handler: |
||||||
|
|
||||||
|
```php |
||||||
|
$component->on($eventName, $handler, $params); |
||||||
|
``` |
||||||
|
|
||||||
|
|
||||||
|
Because of this change, you can now use "global" events. Simply trigger and attach handlers to |
||||||
|
an event of the application instance: |
||||||
|
|
||||||
|
```php |
||||||
|
Yii::$app->on($eventName, $handler); |
||||||
|
.... |
||||||
|
// this will trigger the event and cause $handler to be invoked. |
||||||
|
Yii::$app->trigger($eventName); |
||||||
|
``` |
||||||
|
|
||||||
|
|
||||||
|
Path Alias |
||||||
|
---------- |
||||||
|
|
||||||
|
Yii 2.0 expands the usage of path aliases to both file/directory paths and URLs. An alias |
||||||
|
must start with a `@` character so that it can be differentiated from file/directory paths and URLs. |
||||||
|
For example, the alias `@yii` refers to the Yii installation directory. Path aliases are |
||||||
|
supported in most places in the Yii core code. For example, `FileCache::cachePath` can take |
||||||
|
both a path alias and a normal directory path. |
||||||
|
|
||||||
|
Path alias is also closely related with class namespaces. It is recommended that a path |
||||||
|
alias defined for each root namespace so that you can use Yii class autoloader without |
||||||
|
any further configuration. For example, because `@yii` refers to the Yii installation directory, |
||||||
|
a class like `yii\web\Request` can be autoloaded by Yii. If you use a third party library |
||||||
|
such as Zend Framework, you may define a path alias `@Zend` which refers to its installation directory. |
||||||
|
And Yii will be able to autoload any class in this library. |
||||||
|
|
||||||
|
|
||||||
|
View |
||||||
|
---- |
||||||
|
|
||||||
|
Yii 2.0 introduces a `View` class to represent the view part in the MVC pattern. |
||||||
|
It can be configured globally through the "view" application component. It is also |
||||||
|
accessible in any view file via `$this`. This is one of the biggest changes compared to 1.1: |
||||||
|
**`$this` in a view file no longer refers to the controller or widget object.** |
||||||
|
It refers to the view object that is used to render the view file. To access the controller |
||||||
|
or the widget object, you have to use `$this->context` now. |
||||||
|
|
||||||
|
Because you can access the view object through the "view" application component, |
||||||
|
you can now render a view file like the following anywhere in your code, not necessarily |
||||||
|
in controllers or widgets: |
||||||
|
|
||||||
|
```php |
||||||
|
$content = Yii::$app->view->renderFile($viewFile, $params); |
||||||
|
// You can also explicitly create a new View instance to do the rendering |
||||||
|
// $view = new View; |
||||||
|
// $view->renderFile($viewFile, $params); |
||||||
|
``` |
||||||
|
|
||||||
|
|
||||||
|
Also, there is no more `CClientScript` in Yii 2.0. The `View` class has taken over its role |
||||||
|
with significant improvements. For more details, please see the "assets" subsection. |
||||||
|
|
||||||
|
While Yii 2.0 continues to use PHP as its main template language, it comes with built-in |
||||||
|
support for two popular template engines: Smarty and Twig. The Prado template engine is |
||||||
|
no longer supported. To use these template engines, you just need to use `tpl` as the file |
||||||
|
extension for your Smarty views, or `twig` for Twig views. You may also configure the |
||||||
|
`View::renderers` property to use other template engines. |
||||||
|
|
||||||
|
|
||||||
|
Models |
||||||
|
------ |
||||||
|
|
||||||
|
A model is now associated with a form name returned its `formName()` method. This is |
||||||
|
mainly used when using HTML forms to collect user inputs for a model. Previously in 1.1, |
||||||
|
this is usually hardcoded as the class name of the model. |
||||||
|
|
||||||
|
|
||||||
|
Yii 2.0 introduces a new method called `scenarios()` to declare which attributes require |
||||||
|
validation under which scenario. Child classes should overwrite `scenarios()` to return |
||||||
|
a list of scenarios and the corresponding attributes that need to be validated when |
||||||
|
`validate()` is called. For example, |
||||||
|
|
||||||
|
```php |
||||||
|
public function scenarios() |
||||||
|
{ |
||||||
|
return array( |
||||||
|
'backend' => array('email', 'role'), |
||||||
|
'frontend' => array('email', '!name'), |
||||||
|
); |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
|
||||||
|
This method also determines which attributes are safe and which are not. In particular, |
||||||
|
given a scenario, if an attribute appears in the corresponding attribute list in `scenarios()` |
||||||
|
and the name is not prefixed with `!`, it is considered *safe*. |
||||||
|
|
||||||
|
Because of the above change, Yii 2.0 no longer has "safe" and "unsafe" validators. |
||||||
|
|
||||||
|
If your model only has one scenario (very common), you do not have to overwrite `scenarios()`, |
||||||
|
and everything will still work like the 1.1 way. |
||||||
|
|
||||||
|
|
||||||
|
Controllers |
||||||
|
----------- |
||||||
|
|
||||||
|
The `render()` and `renderPartial()` methods now return the rendering results instead of directly |
||||||
|
sending them out. You have to `echo` them explicitly, e.g., `echo $this->render(...);`. |
||||||
|
|
||||||
|
A new method called `populate()` is introduced to simplify the data population from user inputs |
||||||
|
to a model. For example, |
||||||
|
|
||||||
|
```php |
||||||
|
$model = new Post; |
||||||
|
if ($this->populate($_POST, $model)) {...} |
||||||
|
// which is equivalent to: |
||||||
|
if (isset($_POST['Post'])) { |
||||||
|
$model->attributes = $_POST['Post']; |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Themes |
||||||
|
------ |
||||||
|
|
||||||
|
Theme works completely different in 2.0. It is now based on a path map to "translate" a source |
||||||
|
view into a themed view. For example, if the path map for a theme is |
||||||
|
`array('/www/views' => '/www/themes/basic')`, then the themed version for a view file |
||||||
|
`/www/views/site/index.php` will be `/www/themes/basic/site/index.php`. |
||||||
|
|
||||||
|
For this reason, theme can now be applied to any view file, even if a view rendered outside |
||||||
|
of the context of a controller or a widget. |
||||||
|
|
||||||
|
There is no more `CThemeManager`. Instead, `theme` is a configurable property of the "view" |
||||||
|
application component. |
||||||
|
|
||||||
|
|
||||||
|
Console Applications |
||||||
|
-------------------- |
||||||
|
|
||||||
|
Console applications are now composed by controllers, too, like Web applications. In fact, |
||||||
|
console controllers and Web controllers share the same base controller class. |
||||||
|
|
||||||
|
Each console controller is like `CConsoleCommand` in 1.1. It consists of one or several |
||||||
|
actions. You use the `yiic <route>` command to execute a console command, where `<route>` |
||||||
|
stands for a controller route (e.g. `sitemap/index`). Additional anonymous arguments |
||||||
|
are passed as the parameters to the corresponding controller action method, and named arguments |
||||||
|
are treated as global options declared in `globalOptions()`. |
||||||
|
|
||||||
|
Yii 2.0 supports automatic generation of command help information from comment blocks. |
||||||
|
|
||||||
|
|
||||||
|
I18N |
||||||
|
---- |
||||||
|
|
||||||
|
Yii 2.0 removes date formatter and number formatter in favor of the PECL intl PHP module. |
||||||
|
|
||||||
|
Message translation is still supported, but managed via the "i18n" application component. |
||||||
|
The component manages a set of message sources, which allows you to use different message |
||||||
|
sources based on message categories. For more information, see the class documentation for `I18N`. |
||||||
|
|
||||||
|
The message translation method is changed by merging the message category into the message being |
||||||
|
translated. For example, `Yii::t('yii|message to be translated')`. |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Action Filters |
||||||
|
-------------- |
||||||
|
|
||||||
|
Action filters are implemented via behaviors now. You should extend from `ActionFilter` to |
||||||
|
define a new filter. To use a filter, you should attach the filter class to the controller |
||||||
|
as a behavior. For example, to use the `AccessControl` filter, you should have the following |
||||||
|
code in a controller: |
||||||
|
|
||||||
|
```php |
||||||
|
public function behaviors() |
||||||
|
{ |
||||||
|
return array( |
||||||
|
'access' => array( |
||||||
|
'class' => 'yii\web\AccessControl', |
||||||
|
'rules' => array( |
||||||
|
array('allow' => true, 'actions' => array('admin'), 'roles' => array('@')), |
||||||
|
array('allow' => false), |
||||||
|
), |
||||||
|
), |
||||||
|
); |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Assets |
||||||
|
------ |
||||||
|
|
||||||
|
Yii 2.0 introduces a new concept called *asset bundle*. It is a bit similar to script |
||||||
|
packages (managed by `CClientScript`) in 1.1, but with better support. |
||||||
|
|
||||||
|
An asset bundle is a collection of asset files (e.g. JavaScript files, CSS files, image files, etc.) |
||||||
|
under a directory. By registering an asset bundle via `View::registerAssetBundle()`, you |
||||||
|
will be able to make the assets in that bundle accessible via Web, and the current page |
||||||
|
will automatically contain references to the JavaScript and CSS files in that bundle. |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Static Helpers |
||||||
|
-------------- |
||||||
|
|
||||||
|
Yii 2.0 introduces many commonly used static helper classes, such as `Html`, `ArrayHelper`, |
||||||
|
`StringHelper`. These classes are designed to be easily extended. Note that static classes |
||||||
|
are usually hard to be extended because of the fixed class name references. But Yii 2.0 |
||||||
|
introduces the class map (via `Yii::$classMap`) to overcome this difficulty. |
||||||
|
|
||||||
|
|
||||||
|
`ActiveForm` |
||||||
|
------------ |
||||||
|
|
||||||
|
Yii 2.0 introduces the *field* concept for building a form using `ActiveForm`. A field |
||||||
|
is a container consisting of a label, an input, and an error message. It is represented |
||||||
|
as an `ActiveField` object. Using fields, you can build a form more cleanly than before: |
||||||
|
|
||||||
|
```php |
||||||
|
<?php $form = $this->beginWidget('yii\widgets\ActiveForm'); ?> |
||||||
|
<?php echo $form->field($model, 'username')->textInput(); ?> |
||||||
|
<?php echo $form->field($model, 'password')->passwordInput(); ?> |
||||||
|
<div class="form-actions"> |
||||||
|
<?php echo Html::submitButton('Login'); ?> |
||||||
|
</div> |
||||||
|
<?php $this->endWidget(); ?> |
||||||
|
``` |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Query Builder |
||||||
|
------------- |
||||||
|
|
||||||
|
In 1.1, query building is scattered among several classes, including `CDbCommand`, |
||||||
|
`CDbCriteria`, and `CDbCommandBuilder`. Yii 2.0 uses `Query` to represent a DB query |
||||||
|
and `QueryBuilder` to generate SQL statements from query objects. For example, |
||||||
|
|
||||||
|
```php |
||||||
|
$query = new \yii\db\Query; |
||||||
|
$query->select('id, name') |
||||||
|
->from('tbl_user') |
||||||
|
->limit(10); |
||||||
|
|
||||||
|
$command = $query->createCommand(); |
||||||
|
$sql = $command->sql; |
||||||
|
$rows = $command->queryAll(); |
||||||
|
``` |
||||||
|
|
||||||
|
|
||||||
|
Best of all, such query building methods can be used together with `ActiveRecord`, |
||||||
|
as explained in the next sub-section. |
||||||
|
|
||||||
|
|
||||||
|
ActiveRecord |
||||||
|
------------ |
||||||
|
|
||||||
|
ActiveRecord has undergone significant changes in Yii 2.0. The most important one |
||||||
|
is about relational ActiveRecord query. In 1.1, you have to declare the relations |
||||||
|
in the `relations()` method. In 2.0, this is done via getter methods that return |
||||||
|
an `ActiveQuery` object. For example, the following method declares an "orders" relation: |
||||||
|
|
||||||
|
```php |
||||||
|
class Customer extends \yii\db\ActiveRecord |
||||||
|
{ |
||||||
|
public function getOrders() |
||||||
|
{ |
||||||
|
return $this->hasMany('Order', array('customer_id' => 'id')); |
||||||
|
} |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
|
||||||
|
You can use `$customer->orders` to access the customer's orders. You can also |
||||||
|
use `$customer->getOrders()->andWhere('status=1')->all()` to perform on-the-fly |
||||||
|
relational query with customized query conditions. |
||||||
|
|
||||||
|
When loading relational records in an eager way, Yii 2.0 does it differently from 1.1. |
||||||
|
In particular, in 1.1 a JOIN query would be used to bring both the primary and the relational |
||||||
|
records; while in 2.0, two SQL statements are executed without using JOIN: the first |
||||||
|
statement brings back the primary records and the second brings back the relational records |
||||||
|
by filtering with the primary keys of the primary records. |
||||||
|
|
||||||
|
|
||||||
|
Yii 2.0 no longer uses the `model()` method when performing queries. Instead, you |
||||||
|
use the `find()` method like the following: |
||||||
|
|
||||||
|
```php |
||||||
|
// to retrieve all *active* customers and order them by their ID: |
||||||
|
$customers = Customer::find() |
||||||
|
->where(array('status' => $active)) |
||||||
|
->orderBy('id') |
||||||
|
->all(); |
||||||
|
// return the customer whose PK is 1 |
||||||
|
$customer = Customer::find(1); |
||||||
|
``` |
||||||
|
|
||||||
|
|
||||||
|
The `find()` method returns an instance of `ActiveQuery` which is a subclass of `Query`. |
||||||
|
Therefore, you can use all query methods of `Query`. |
||||||
|
|
||||||
|
Instead of returning ActiveRecord objects, you may call `ActiveQuery::asArray()` to |
||||||
|
return results in terms of arrays. This is more efficient and is especially useful |
||||||
|
when you need to return large number of records. For example, |
||||||
|
|
||||||
|
```php |
||||||
|
$customers = Customer::find()->asArray()->all(); |
||||||
|
``` |
||||||
|
|
||||||
|
By default, ActiveRecord now only saves dirty attributes. In 1.1, all attributes |
||||||
|
would be saved to database when you call `save()`, regardless they are changed or not, |
||||||
|
unless you explicitly list the attributes to save. |
||||||
|
|
||||||
|
|
||||||
|
Auto-quoting Table and Column Names |
||||||
|
------------------------------------ |
||||||
|
|
||||||
|
Yii 2.0 supports automatic quoting of database table and column names. A name enclosed |
||||||
|
within double curly brackets is treated as a table name, and a name enclosed within |
||||||
|
double square brackets is treated as a column name. They will be quoted according to |
||||||
|
the database driver being used. For example, |
||||||
|
|
||||||
|
```php |
||||||
|
$command = $connection->createCommand('SELECT [[id]] FROM {{posts}}'); |
||||||
|
echo $command->sql; // MySQL: SELECT `id` FROM `posts` |
||||||
|
``` |
||||||
|
|
||||||
|
This feature is especially useful if you are developing an application that supports |
||||||
|
different DBMS. |
||||||
|
|
||||||
|
|
||||||
|
User and Identity |
||||||
|
----------------- |
||||||
|
|
||||||
|
The `CWebUser` class in 1.1 is now replaced by `\yii\Web\User`, and there is no more |
||||||
|
`CUserIdentity` class. Instead, you should implement the `Identity` interface which |
||||||
|
is much more straightforward to implement. The bootstrap application provides such an example. |
||||||
|
|
||||||
|
|
||||||
|
URL Management |
||||||
|
-------------- |
||||||
|
|
||||||
|
URL management is similar to 1.1. A major enhancement is that it now supports optional |
||||||
|
parameters. For example, if you have rule declared as follows, then it will match |
||||||
|
both `post/popular` and `post/1/popular`. In 1.1, you would have to use two rules to achieve |
||||||
|
the same goal. |
||||||
|
|
||||||
|
```php |
||||||
|
array( |
||||||
|
'pattern' => 'post/<page:\d+>/<tag>', |
||||||
|
'route' => 'post/index', |
||||||
|
'defaults' => array('page' => 1), |
||||||
|
) |
||||||
|
``` |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Response |
||||||
|
-------- |
||||||
|
|
||||||
|
Extensions |
||||||
|
---------- |
||||||
|
|
||||||
|
Integration with Composer |
||||||
|
------------------------- |
||||||
|
|
||||||
|
TBD |
||||||
|
|
@ -0,0 +1,139 @@ |
|||||||
|
<?php |
||||||
|
/** |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright Copyright (c) 2008 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
|
||||||
|
namespace yii\widgets; |
||||||
|
|
||||||
|
use Yii; |
||||||
|
use yii\base\Widget; |
||||||
|
use yii\base\InvalidConfigException; |
||||||
|
use yii\helpers\Html; |
||||||
|
|
||||||
|
/** |
||||||
|
* Breadcrumbs displays a list of links indicating the position of the current page in the whole site hierarchy. |
||||||
|
* |
||||||
|
* For example, breadcrumbs like "Home / Sample Post / Edit" means the user is viewing an edit page |
||||||
|
* for the "Sample Post". He can click on "Sample Post" to view that page, or he can click on "Home" |
||||||
|
* to return to the homepage. |
||||||
|
* |
||||||
|
* To use Breadcrumbs, you need to configure its [[links]] property, which specifiesthe links to be displayed. For example, |
||||||
|
* |
||||||
|
* ~~~ |
||||||
|
* $this->widget('yii\widgets\Breadcrumbs', array( |
||||||
|
* 'links' => array( |
||||||
|
* array('label' => 'Sample Post', 'url' => array('post/edit', 'id' => 1)), |
||||||
|
* 'Edit', |
||||||
|
* ), |
||||||
|
* )); |
||||||
|
* ~~~ |
||||||
|
* |
||||||
|
* Because breadcrumbs usually appears in nearly every page of a website, you may consider place it in a layout view. |
||||||
|
* You can then use a view parameter (e.g. `$this->params['breadcrumbs']`) to configure the links in different |
||||||
|
* views. In the layout view, you assign this view parameter to the [[links]] property like the following: |
||||||
|
* |
||||||
|
* ~~~ |
||||||
|
* $this->widget('yii\widgets\Breadcrumbs', array( |
||||||
|
* 'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : array(), |
||||||
|
* )); |
||||||
|
* ~~~ |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @since 2.0 |
||||||
|
*/ |
||||||
|
class Breadcrumbs extends Widget |
||||||
|
{ |
||||||
|
/** |
||||||
|
* @var string the name of the breadcrumb container tag. |
||||||
|
*/ |
||||||
|
public $tag = 'ul'; |
||||||
|
/** |
||||||
|
* @var array the HTML attributes for the breadcrumb container tag. |
||||||
|
*/ |
||||||
|
public $options = array('class' => 'breadcrumb'); |
||||||
|
/** |
||||||
|
* @var boolean whether to HTML-encode the link labels. |
||||||
|
*/ |
||||||
|
public $encodeLabels = true; |
||||||
|
/** |
||||||
|
* @var string the first hyperlink in the breadcrumbs (called home link). |
||||||
|
* If this property is not set, it will default to a link pointing to [[\yii\web\Application::homeUrl]] |
||||||
|
* with the label 'Home'. If this property is false, the home link will not be rendered. |
||||||
|
*/ |
||||||
|
public $homeLink; |
||||||
|
/** |
||||||
|
* @var array list of links to appear in the breadcrumbs. If this property is empty, |
||||||
|
* the widget will not render anything. Each array element represents a single link in the breadcrumbs |
||||||
|
* with the following structure: |
||||||
|
* |
||||||
|
* ~~~ |
||||||
|
* array( |
||||||
|
* 'label' => 'label of the link', // required |
||||||
|
* 'url' => 'url of the link', // optional, will be processed by Html::url() |
||||||
|
* ) |
||||||
|
* ~~~ |
||||||
|
* |
||||||
|
* If a link is active, you only need to specify its "label", and instead of writing `array('label' => $label)`, |
||||||
|
* you should simply use `$label`. |
||||||
|
*/ |
||||||
|
public $links = array(); |
||||||
|
/** |
||||||
|
* @var string the template used to render each inactive item in the breadcrumbs. The token `{link}` |
||||||
|
* will be replaced with the actual HTML link for each inactive item. |
||||||
|
*/ |
||||||
|
public $itemTemplate = "<li>{link} <span class=\"divider\">/</span></li>\n"; |
||||||
|
/** |
||||||
|
* @var string the template used to render each active item in the breadcrumbs. The token `{link}` |
||||||
|
* will be replaced with the actual HTML link for each active item. |
||||||
|
*/ |
||||||
|
public $activeItemTemplate = "<li class=\"active\">{link}</li>\n"; |
||||||
|
|
||||||
|
/** |
||||||
|
* Renders the widget. |
||||||
|
*/ |
||||||
|
public function run() |
||||||
|
{ |
||||||
|
if (empty($this->links)) { |
||||||
|
return; |
||||||
|
} |
||||||
|
$links = array(); |
||||||
|
if ($this->homeLink === null) { |
||||||
|
$links[] = $this->renderItem(array( |
||||||
|
'label' => Yii::t('yii|Home'), |
||||||
|
'url' => Yii::$app->homeUrl, |
||||||
|
), $this->itemTemplate); |
||||||
|
} elseif ($this->homeLink !== false) { |
||||||
|
$links[] = $this->renderItem($this->homeLink, $this->itemTemplate); |
||||||
|
} |
||||||
|
foreach ($this->links as $link) { |
||||||
|
if (!is_array($link)) { |
||||||
|
$link = array('label' => $link); |
||||||
|
} |
||||||
|
$links[] = $this->renderItem($link, isset($link['url']) ? $this->itemTemplate : $this->activeItemTemplate); |
||||||
|
} |
||||||
|
echo Html::tag($this->tag, implode('', $links), $this->options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Renders a single breadcrumb item. |
||||||
|
* @param array $link the link to be rendered. It must contain the "label" element. The "url" element is optional. |
||||||
|
* @param string $template the template to be used to rendered the link. The token "{link}" will be replaced by the link. |
||||||
|
* @return string the rendering result |
||||||
|
* @throws InvalidConfigException if `$link` does not have "label" element. |
||||||
|
*/ |
||||||
|
protected function renderItem($link, $template) |
||||||
|
{ |
||||||
|
if (isset($link['label'])) { |
||||||
|
$label = $this->encodeLabels ? Html::encode($link['label']) : $link['label']; |
||||||
|
} else { |
||||||
|
throw new InvalidConfigException('The "label" element is required for each link.'); |
||||||
|
} |
||||||
|
if (isset($link['url'])) { |
||||||
|
return strtr($template, array('{link}' => Html::a($label, $link['url']))); |
||||||
|
} else { |
||||||
|
return strtr($template, array('{link}' => $label)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,282 @@ |
|||||||
|
<?php |
||||||
|
/** |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright Copyright (c) 2008 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
|
||||||
|
namespace yii\widgets; |
||||||
|
|
||||||
|
use yii\base\Widget; |
||||||
|
use yii\helpers\Html; |
||||||
|
|
||||||
|
/** |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @since 2.0 |
||||||
|
*/ |
||||||
|
class Menu extends Widget |
||||||
|
{ |
||||||
|
/** |
||||||
|
* @var array list of menu items. Each menu item is specified as an array of name-value pairs. |
||||||
|
* Possible option names include the following: |
||||||
|
* <ul> |
||||||
|
* <li>label: string, optional, specifies the menu item label. When {@link encodeLabel} is true, the label |
||||||
|
* will be HTML-encoded. If the label is not specified, it defaults to an empty string.</li> |
||||||
|
* <li>url: string or array, optional, specifies the URL of the menu item. It is passed to {@link Html::normalizeUrl} |
||||||
|
* to generate a valid URL. If this is not set, the menu item will be rendered as a span text.</li> |
||||||
|
* <li>visible: boolean, optional, whether this menu item is visible. Defaults to true. |
||||||
|
* This can be used to control the visibility of menu items based on user permissions.</li> |
||||||
|
* <li>items: array, optional, specifies the sub-menu items. Its format is the same as the parent items.</li> |
||||||
|
* <li>active: boolean, optional, whether this menu item is in active state (currently selected). |
||||||
|
* If a menu item is active and {@link activeClass} is not empty, its CSS class will be appended with {@link activeClass}. |
||||||
|
* If this option is not set, the menu item will be set active automatically when the current request |
||||||
|
* is triggered by {@link url}. Note that the GET parameters not specified in the 'url' option will be ignored.</li> |
||||||
|
* <li>template: string, optional, the template used to render this menu item. |
||||||
|
* When this option is set, it will override the global setting {@link itemTemplate}. |
||||||
|
* Please see {@link itemTemplate} for more details. This option has been available since version 1.1.1.</li> |
||||||
|
* <li>linkOptions: array, optional, additional HTML attributes to be rendered for the link or span tag of the menu item.</li> |
||||||
|
* <li>itemOptions: array, optional, additional HTML attributes to be rendered for the container tag of the menu item.</li> |
||||||
|
* <li>submenuOptions: array, optional, additional HTML attributes to be rendered for the container of the submenu if this menu item has one. |
||||||
|
* When this option is set, the {@link submenuHtmlOptions} property will be ignored for this particular submenu. |
||||||
|
* This option has been available since version 1.1.6.</li> |
||||||
|
* </ul> |
||||||
|
*/ |
||||||
|
public $items = array(); |
||||||
|
/** |
||||||
|
* @var string the template used to render an individual menu item. In this template, |
||||||
|
* the token "{menu}" will be replaced with the corresponding menu link or text. |
||||||
|
* If this property is not set, each menu will be rendered without any decoration. |
||||||
|
* This property will be overridden by the 'template' option set in individual menu items via {@items}. |
||||||
|
* @since 1.1.1 |
||||||
|
*/ |
||||||
|
public $itemTemplate; |
||||||
|
/** |
||||||
|
* @var boolean whether the labels for menu items should be HTML-encoded. Defaults to true. |
||||||
|
*/ |
||||||
|
public $encodeLabel = true; |
||||||
|
/** |
||||||
|
* @var string the CSS class to be appended to the active menu item. Defaults to 'active'. |
||||||
|
* If empty, the CSS class of menu items will not be changed. |
||||||
|
*/ |
||||||
|
public $activeCssClass = 'active'; |
||||||
|
/** |
||||||
|
* @var boolean whether to automatically activate items according to whether their route setting |
||||||
|
* matches the currently requested route. Defaults to true. |
||||||
|
* @since 1.1.3 |
||||||
|
*/ |
||||||
|
public $activateItems = true; |
||||||
|
/** |
||||||
|
* @var boolean whether to activate parent menu items when one of the corresponding child menu items is active. |
||||||
|
* The activated parent menu items will also have its CSS classes appended with {@link activeCssClass}. |
||||||
|
* Defaults to false. |
||||||
|
*/ |
||||||
|
public $activateParents = false; |
||||||
|
/** |
||||||
|
* @var boolean whether to hide empty menu items. An empty menu item is one whose 'url' option is not |
||||||
|
* set and which doesn't contain visible child menu items. Defaults to true. |
||||||
|
*/ |
||||||
|
public $hideEmptyItems = true; |
||||||
|
/** |
||||||
|
* @var array HTML attributes for the menu's root container tag |
||||||
|
*/ |
||||||
|
public $options = array(); |
||||||
|
/** |
||||||
|
* @var array HTML attributes for the submenu's container tag. |
||||||
|
*/ |
||||||
|
public $submenuHtmlOptions = array(); |
||||||
|
/** |
||||||
|
* @var string the HTML element name that will be used to wrap the label of all menu links. |
||||||
|
* For example, if this property is set as 'span', a menu item may be rendered as |
||||||
|
* <li><a href="url"><span>label</span></a></li> |
||||||
|
* This is useful when implementing menu items using the sliding window technique. |
||||||
|
* Defaults to null, meaning no wrapper tag will be generated. |
||||||
|
* @since 1.1.4 |
||||||
|
*/ |
||||||
|
public $linkLabelWrapper; |
||||||
|
/** |
||||||
|
* @var array HTML attributes for the links' wrap element specified in |
||||||
|
* {@link linkLabelWrapper}. |
||||||
|
* @since 1.1.13 |
||||||
|
*/ |
||||||
|
public $linkLabelWrapperHtmlOptions = array(); |
||||||
|
/** |
||||||
|
* @var string the CSS class that will be assigned to the first item in the main menu or each submenu. |
||||||
|
* Defaults to null, meaning no such CSS class will be assigned. |
||||||
|
* @since 1.1.4 |
||||||
|
*/ |
||||||
|
public $firstItemCssClass; |
||||||
|
/** |
||||||
|
* @var string the CSS class that will be assigned to the last item in the main menu or each submenu. |
||||||
|
* Defaults to null, meaning no such CSS class will be assigned. |
||||||
|
* @since 1.1.4 |
||||||
|
*/ |
||||||
|
public $lastItemCssClass; |
||||||
|
/** |
||||||
|
* @var string the CSS class that will be assigned to every item. |
||||||
|
* Defaults to null, meaning no such CSS class will be assigned. |
||||||
|
* @since 1.1.9 |
||||||
|
*/ |
||||||
|
public $itemCssClass; |
||||||
|
|
||||||
|
/** |
||||||
|
* Initializes the menu widget. |
||||||
|
* This method mainly normalizes the {@link items} property. |
||||||
|
* If this method is overridden, make sure the parent implementation is invoked. |
||||||
|
*/ |
||||||
|
public function init() |
||||||
|
{ |
||||||
|
$route = $this->getController()->getRoute(); |
||||||
|
$this->items = $this->normalizeItems($this->items, $route, $hasActiveChild); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Calls {@link renderMenu} to render the menu. |
||||||
|
*/ |
||||||
|
public function run() |
||||||
|
{ |
||||||
|
if (count($this->items)) { |
||||||
|
echo Html::beginTag('ul', $this->options) . "\n"; |
||||||
|
$this->renderItems($this->items); |
||||||
|
echo Html::endTag('ul'); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Recursively renders the menu items. |
||||||
|
* @param array $items the menu items to be rendered recursively |
||||||
|
*/ |
||||||
|
protected function renderItems($items) |
||||||
|
{ |
||||||
|
$count = 0; |
||||||
|
$n = count($items); |
||||||
|
foreach ($items as $item) { |
||||||
|
$count++; |
||||||
|
$options = isset($item['itemOptions']) ? $item['itemOptions'] : array(); |
||||||
|
$class = array(); |
||||||
|
if ($item['active'] && $this->activeCssClass != '') { |
||||||
|
$class[] = $this->activeCssClass; |
||||||
|
} |
||||||
|
if ($count === 1 && $this->firstItemCssClass !== null) { |
||||||
|
$class[] = $this->firstItemCssClass; |
||||||
|
} |
||||||
|
if ($count === $n && $this->lastItemCssClass !== null) { |
||||||
|
$class[] = $this->lastItemCssClass; |
||||||
|
} |
||||||
|
if ($this->itemCssClass !== null) { |
||||||
|
$class[] = $this->itemCssClass; |
||||||
|
} |
||||||
|
if ($class !== array()) { |
||||||
|
if (empty($options['class'])) { |
||||||
|
$options['class'] = implode(' ', $class); |
||||||
|
} else { |
||||||
|
$options['class'] .= ' ' . implode(' ', $class); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
echo Html::beginTag('li', $options); |
||||||
|
|
||||||
|
$menu = $this->renderItem($item); |
||||||
|
if (isset($this->itemTemplate) || isset($item['template'])) { |
||||||
|
$template = isset($item['template']) ? $item['template'] : $this->itemTemplate; |
||||||
|
echo strtr($template, array('{menu}' => $menu)); |
||||||
|
} else { |
||||||
|
echo $menu; |
||||||
|
} |
||||||
|
|
||||||
|
if (isset($item['items']) && count($item['items'])) { |
||||||
|
echo "\n" . Html::beginTag('ul', isset($item['submenuOptions']) ? $item['submenuOptions'] : $this->submenuHtmlOptions) . "\n"; |
||||||
|
$this->renderItems($item['items']); |
||||||
|
echo Html::endTag('ul') . "\n"; |
||||||
|
} |
||||||
|
|
||||||
|
echo Html::endTag('li') . "\n"; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Renders the content of a menu item. |
||||||
|
* Note that the container and the sub-menus are not rendered here. |
||||||
|
* @param array $item the menu item to be rendered. Please see {@link items} on what data might be in the item. |
||||||
|
* @return string |
||||||
|
* @since 1.1.6 |
||||||
|
*/ |
||||||
|
protected function renderItem($item) |
||||||
|
{ |
||||||
|
if (isset($item['url'])) { |
||||||
|
$label = $this->linkLabelWrapper === null ? $item['label'] : Html::tag($this->linkLabelWrapper, $this->linkLabelWrapperHtmlOptions, $item['label']); |
||||||
|
return Html::a($label, $item['url'], isset($item['linkOptions']) ? $item['linkOptions'] : array()); |
||||||
|
} else { |
||||||
|
return Html::tag('span', isset($item['linkOptions']) ? $item['linkOptions'] : array(), $item['label']); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Normalizes the {@link items} property so that the 'active' state is properly identified for every menu item. |
||||||
|
* @param array $items the items to be normalized. |
||||||
|
* @param string $route the route of the current request. |
||||||
|
* @param boolean $active whether there is an active child menu item. |
||||||
|
* @return array the normalized menu items |
||||||
|
*/ |
||||||
|
protected function normalizeItems($items, $route, &$active) |
||||||
|
{ |
||||||
|
foreach ($items as $i => $item) { |
||||||
|
if (isset($item['visible']) && !$item['visible']) { |
||||||
|
unset($items[$i]); |
||||||
|
continue; |
||||||
|
} |
||||||
|
if (!isset($item['label'])) { |
||||||
|
$item['label'] = ''; |
||||||
|
} |
||||||
|
if ($this->encodeLabel) { |
||||||
|
$items[$i]['label'] = Html::encode($item['label']); |
||||||
|
} |
||||||
|
$hasActiveChild = false; |
||||||
|
if (isset($item['items'])) { |
||||||
|
$items[$i]['items'] = $this->normalizeItems($item['items'], $route, $hasActiveChild); |
||||||
|
if (empty($items[$i]['items']) && $this->hideEmptyItems) { |
||||||
|
unset($items[$i]['items']); |
||||||
|
if (!isset($item['url'])) { |
||||||
|
unset($items[$i]); |
||||||
|
continue; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
if (!isset($item['active'])) { |
||||||
|
if ($this->activateParents && $hasActiveChild || $this->activateItems && $this->isItemActive($item, $route)) { |
||||||
|
$active = $items[$i]['active'] = true; |
||||||
|
} else { |
||||||
|
$items[$i]['active'] = false; |
||||||
|
} |
||||||
|
} elseif ($item['active']) { |
||||||
|
$active = true; |
||||||
|
} |
||||||
|
} |
||||||
|
return array_values($items); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Checks whether a menu item is active. |
||||||
|
* This is done by checking if the currently requested URL is generated by the 'url' option |
||||||
|
* of the menu item. Note that the GET parameters not specified in the 'url' option will be ignored. |
||||||
|
* @param array $item the menu item to be checked |
||||||
|
* @param string $route the route of the current request |
||||||
|
* @return boolean whether the menu item is active |
||||||
|
*/ |
||||||
|
protected function isItemActive($item, $route) |
||||||
|
{ |
||||||
|
if (isset($item['url']) && is_array($item['url']) && !strcasecmp(trim($item['url'][0], '/'), $route)) { |
||||||
|
unset($item['url']['#']); |
||||||
|
if (count($item['url']) > 1) { |
||||||
|
foreach (array_splice($item['url'], 1) as $name => $value) { |
||||||
|
if (!isset($_GET[$name]) || $_GET[$name] != $value) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
} |
Loading…
Reference in new issue