Yii 2.0 Beta is released

Apr 13, 2014

We are very pleased to announce the Beta release of Yii Framework version 2. You can download it from yiiframework.com.

This Beta release includes hundreds of new features, changes and bug fixes that have been made since the alpha release. We will review the most important ones in the following. But first, we would like to answer some commonly asked questions regarding Beta.

Commonly Asked Questions

  • What does Beta mean? Beta means feature and design freeze. After Beta and before GA (General Availability), we will mainly focus on fixing bugs and finishing documentation. We will no longer introduce major new features or significant design changes. There may still be changes that will break BC (Backward Compatibility), but we will try to minimize them and will record clearly the BC-breaking changes.

  • When will GA be released? We do not have an exact date for that yet. Since our focus next is mainly on bug fixes and documentation, we expect it will not take long to reach GA.

  • Can I use Beta for my projects? Do not use Beta if your project is on a tight schedule and you are not familiar with Yii 2.0 yet. Otherwise, you may consider using Yii 2 Beta, provided that you are comfortable with occasional BC-breaking changes. We have heard there are already many projects built on 2.0 master and are working well. Also keep in mind that the minimum PHP version required is 5.4.

  • Are there any documentation for 2.0? Yes, we have The Definitive Guide and the API documentation. And we are still adding more contents to the former.

  • How can I upgrade my applications written in 1.1 to 2.0? Please refer to Upgrading from Yii 1.1. Note that since 2.0 is a complete rewrite of 1.1, the upgrade will not be trivial. However, if you are familiar with 1.1, you will find many similarities in 2.0, which should help you to adopt 2.0 more quickly.

  • How can I upgrade from 2.0 alpha? If you are updating alpha version via Composer you need to remove everything except .gitignore from vendor directory and re-run composer. This is a one-time thing that will not be required for any future releases. Please check the CHANGELOG file in the release to find out more details about the BC-breaking changes.

  • How can I follow the 2.0 development? All development activities of Yii 2.0 can be found on GitHub: https://github.com/yiisoft/yii2. You may watch or star this project to receive development updates. You may also follow our twitter updates at https://twitter.com/yiiframework.

Major Changes since 2.0 Alpha

You may find a complete list of changes in the CHANGELOG. Below we are summarizing the most important new features and changes.

Structure

Yii 2 now follows the PSR-4 standard for its class autoloading. This results in three improvements:

  • Simpler framework directory structure.
  • Simpler extensions directory structure.
  • We've dropped PEAR-style class naming resulting in simpler and faster autoloading.

Controller classes are now required to be namespaced and must be located under Module::controllerNamespace, unless you use controller mapping via Module::controllerMap.

We have added back the support for grouping controllers by subdirectories, which is also supported in 1.1.

Usability

Usability is one of the highest priorities for the Yii team. That's why we're spending lots of time choosing good names for everything, making code work with IDEs better resulting in more pleasant daily development experience.

We've adopted PSR-1 and PSR-2 and got out of the box support from various IDEs, code style checkers and automatic formatters.

Performance

The most notable change is that session is not started until it is actually used. This allows applications to not waste resources on starting sessions unnecessarily.

If you are using MarkDown in your project, you may find the MarkDown formatting speed is significantly improved. This is because Carsten Brandt (cebe) built a new MarkDown library from scratch after analyzing all existing solutions. The new library is much fast and is easier to be extended. It also supports GitHub flavored format and many other features.

Security

Yii now uses masked CSRF tokens to prevent BREACH type of exploits.

RBAC biz rules were refactored, which results in a more flexible yet safer solution. We have eliminated the use of eval() for biz rules.

RESTful API framework

A long wanted feature in Yii is the built-in support for RESTful API development. This finally came into reality with the Beta release. Due to the limit of this article, we will not expand the details here. You may refer to The Definitive Guide for details. Below we mainly summarize the supported features as of now:

  • Quick prototyping with support for common APIs for ActiveRecord;
  • Response format negotiation (supporting JSON and XML by default);
  • Customizable object serialization with support for selectable output fields;
  • Proper formatting of collection data and validation errors;
  • Efficient routing with proper HTTP verb check;
  • Support for OPTIONS and HEAD verbs;
  • Authentication;
  • Authorization;
  • Support for HATEOAS;
  • HTTP Caching;
  • Rate limiting.

Dependency Injection and Service Locator

Many users were asking why Yii does not provide a Dependency Injection (DI) Container. The fact is that Yii has long been providing a similar facility known as Service Locator - the Yii application instance. Now we have formally extracted out the service locator as a reusable component yii\di\ServiceLocator. Like before, the Yii application and also modules are both service locators. You may obtain a service (aka. application component in 1.1 terminology) using the expression Yii::$app->get('something').

Besides Service Locator, we also implemented a DI Container yii\di\Container to help you develop code in a less coupled way. Our internal profiling shows this DI container is one of the fastest among most notable PHP DI implementations. You may use Yii::$container->set() to configure default settings of classes. The old Yii::$objectConfig is dropped in favor of this new implementation.

Testing

Yii got integration with the Codeception testing framework. It allows you to test an application as a whole simulating user actions and verifying if resulting output is correct. In contrast with PhpUnit's selenium support it doesn't require a browser so it's easier to install for CI server and runs much faster.

Yii also added more support for building test fixtures, which is often a tedious and time consuming task when building tests. In particular, a fixture framework is developed to unify the fixture definition and management. We created the faker extension by integrating the "faker" library to help you create some realistically-looking faked fixture data.

Both the "basic" and "advanced" application templates now come with tests, including unit tests, functionality tests and acceptance tests. This will give a good start for Test-Driven development.

Model Validation

There are many useful enhancements to the model validation feature.

The UniqueValidator and ExistValidator now support validating multiple columns. Below are some examples about the unique validation rule declaration:

// a1 needs to be unique
['a1', 'unique']
 
// a1 needs to be unique, but column a2 will be used to check the uniqueness of the a1 value
['a1', 'unique', 'targetAttribute' => 'a2']
 
// a1 and a2 need to be unique together, and they both will receive an error message
[['a1', 'a2'], 'unique', 'targetAttribute' => ['a1', 'a2']]
 
// a1 and a2 need to unique together, only a1 will receive the error message
['a1', 'unique', 'targetAttribute' => ['a1', 'a2']]
 
// a1 needs to be unique by checking the uniqueness of both a2 and a3 (using a1 value)
['a1', 'unique', 'targetAttribute' => ['a2', 'a1' => 'a3']]

Validations can be done conditionally (aka. conditional validation). This is supported by the addition of two properties when and whenClient to each validator. The following example shows how to require the "state" input only when the country is selected as "USA":

['state', 'required',
    'when' => function ($model) {
        return $model->country == Country::USA;
    },
    'whenClient' =>  "function (attribute, value) {
        return $('#country').value == 'USA';
    }",
]

Sometimes, you may want to do some ad-hoc data validation without the trouble of writing new model classes. You can accomplish this with the help of the new yii\base\DynamicModel. For example,

public function actionSearch($name, $email)
{
    $model = DynamicModel::validateData(compact('name', 'email'), [
        [['name', 'email'], 'string', 'max' => 128],
        ['email', 'email'],
    ]);
    if ($model->hasErrors()) {
        // validation fails
    } else {
        // validation succeeds
    }
}

Database and Active Record

Database-related features are one of the strongest sides of Yii. They were quite interesting when alpha was released and now beta brought more improvements and features. Among support for SQL databases we have ActiveRecord implementations for elasticsearch, redis and Sphinx search already. The Beta version now brings support for the mongodb document storage.

Nested Transaction Support

Yii now supports nested transactions. As a result, you can safely start a transaction without worrying if there is already an existing transaction enclosing it.

Join Queries

We added ActiveQuery::joinWith() to support creating JOIN SQL statements using the AR relations you have already declared. This is especially useful when you want to filter or sort by columns from foreign tables. For example,

// find all orders and sort the orders by the customer id and the order id. also eager loading "customer"
$orders = Order::find()->joinWith('customer')->orderBy('customer.id, order.id')->all();
 
// find all orders that contain books, and eager loading "books"
$orders = Order::find()->innerJoinWith('books')->all();

This feature is especially useful when displaying relational columns in a GridView. It became very easy making them sortable and filterable using joinWith().

Data Typecasting

ActiveRecord will now convert data retrieved from the database to proper types. For example, if you have an integer column type, after the corresponding ActiveRecord instance is populated, you will find the type attribute gets an integer value, rather than a string value.

Searching

To facilitate building search functionality, we have added the Query::filterWhere() method which will automatically remove empty filter values. For example, if you have a search form with name and email filter fields. You may use the following code to build the search query. Without this method, you would have to check if the user has entered anything in a filter field, and if not you will not put it in the query condition. filterWhere() will only add non-empty fields to the condition.

$query = User::find()->filterWhere([
    'name' => Yii::$app->request->get('name'),
    'email' => Yii::$app->request->get('email'),
]);

Batch Query

To support big data query, we have added the batch query feature which brings back data in batches instead of all at once. This allows you to keep the server memory usage under a limit. For example,

use yii\db\Query;
 
$query = (new Query())
    ->from('user')
    ->orderBy('id');
 
foreach ($query->batch() as $users) {
    // $users is an array of 100 or fewer rows from the user table
}
 
// or if you want to iterate the row one by one
foreach ($query->each() as $user) {
    // $user represents one row of data from the user table
}

You may use batch with ActiveRecord too. For example,

// fetch 10 customers at a time
foreach (Customer::find()->batch(10) as $customers) {
    // $customers is an array of 10 or fewer Customer objects
}
// fetch 10 customers at a time and iterate them one by one
foreach (Customer::find()->each(10) as $customer) {
    // $customer is a Customer object
}
// batch query with eager loading
foreach (Customer::find()->with('orders')->each() as $customer) {
}

Support for Sub-queries

The query builder has been improved to support sub-queries. You may build a sub-query as a normal Query object and then use it in appropriate places in another query. For example,

$subQuery = (new Query())->select('id')->from('user')->where('status=1');
$query->select('*')->from(['u' => $subQuery]);

Inverse Relations

Relations can often be defined in pairs. For example, Customer may have a relation named orders while Order may have a relation named customer. In the following example, we may find that the customer of an order is not the same customer object that finds those orders, and accessing customer->orders will trigger one SQL execution while accessing the customer of an order will trigger another SQL execution:

// SELECT * FROM customer WHERE id=1
$customer = Customer::findOne(1);
// echoes "not equal"
// SELECT * FROM order WHERE customer_id=1
// SELECT * FROM customer WHERE id=1
if ($customer->orders[0]->customer === $customer) {
    echo 'equal';
} else {
    echo 'not equal';
}

To avoid the redundant execution of the last SQL statement, we could declare the inverse relation for the customer and the orders relations by calling the inverseOf() method, like the following:

class Customer extends ActiveRecord
{
    // ...
    public function getOrders()
    {
        return $this->hasMany(Order::className(), ['customer_id' => 'id'])->inverseOf('customer');
    }
}

Now if we execute the same query as shown above, we would get:

// SELECT * FROM customer WHERE id=1
$customer = Customer::findOne(1);
// echoes "equal"
// SELECT * FROM order WHERE customer_id=1
if ($customer->orders[0]->customer === $customer) {
    echo 'equal';
} else {
    echo 'not equal';
}

More Consistent Relational Query APIs

In 2.0 alpha, we have introduced ActiveRecord support for both relational (e.g. MySQL) and NoSQL databases (e.g. redis, elasticsearch, MongoDB). In Beta, we refactored the relevant code to make the interfaces more consistent. In particular, we dropped ActiveRelation and made ActiveQuery the sole entry point for building ActiveRecord relational queries and declaring relations. We also added ActiveRecord::findOne() and findAll() to support quick query by primary keys or column values. Previously, the same functionality was assumed by ActiveRecord::find() which sometimes caused confusion due to inconsistent return types.

Advanced Ajax Support

We have decided to use the excellent Pjax library and created the yii\widgets\Pjax widget. This is a generic widget that can enable ajax support for anything it encloses. For example, you can enclose a GridView with Pjax to enable ajax-based grid pagination and sorting:

use yii\widgets\Pjax;
use yii\grid\GridView;
 
Pjax::begin();
echo GridView::widget([ /*...*/ ]);
Pjax::end();

Request and response

Besides many internal bug fixes and improvements request and response got some significant changes. Most notably working with request data now looks like the following:

// take a GET parameter from the request, defaults to 1 if not given
$page = Yii::$app->request->get('page', 1);
// take a POST parameter from the request, defaults to null if not given
$name = Yii::$app->request->post('name');

Another fundamental change is that response is actually sent at the very end of application lifecycle allowing you to modify headers and content as you like and where you prefer.

The request class is now also able to parse different body types for example JSON requests.

Filters

The whole action filtering mechanism has been revamped. You can now enable action filtering at controller level as well as application and module levels. This allows you to filter action flow hierarchically. For example, you can install a filter in a module so that all actions within the module are subject to this filter; and you can further install another filter in some of the controllers in the module so that only actions in those controllers will be filtered.

We have reorganized our code and created a whole set of filters under the yii\filters namespace. For example, you can use yii\filters\HttpBasicAtuh filter to enable authentication based on HTTP Basic Auth by declaring it in a controller or module:

public function behaviors()
{
    return [
        'basicAuth' => [
            'class' => \yii\filters\auth\HttpBasicAuth::className(),
            'exclude'=> ['error'],   // do not apply it to the "error" action
        ],
    ];
}

Bootstrap Components

We introduce the important "bootstrap" step in the application life cycle. Extensions can register bootstrap classes by declaring them in the composer.json file. A normal component can also be registered as a bootstrap component as long as it is declared in Application::$bootstrap.

A bootstrap component will be instantiated before the application starts to process a request. This gives the component the opportunity to register handlers to important events and participate in the application life cycle.

URL Handling

Since developers are dealing with URLs a lot we've extracted most of URL-related methods into a Url helper class resulting in a nicer API.

use yii\helpers\Url;
 
// currently active route
// example: /index.php?r=management/default/users
echo Url::to('');
 
// same controller, different action
// example: /index.php?r=management/default/page&id=contact
echo Url::toRoute(['page', 'id' => 'contact']);
 
 
// same module, different controller and action
// example: /index.php?r=management/post/index
echo Url::toRoute('post/index');
 
// absolute route no matter what controller is making this call
// example: /index.php?r=site/index
echo Url::toRoute('/site/index');
 
// url for the case sensitive action `actionHiTech` of the current controller
// example: /index.php?r=management/default/hi-tech
echo Url::toRoute('hi-tech');
 
// url for action the case sensitive controller, `DateTimeController::actionFastForward`
// example: /index.php?r=date-time/fast-forward&id=105
echo Url::toRoute(['/date-time/fast-forward', 'id' => 105]);
 
// get URL from alias
Yii::setAlias('@google', 'http://google.com/');
echo Url::to('@google/?q=yii');
 
// get canonical URL for the curent page
// example: /index.php?r=management/default/users
echo Url::canonical();
 
// get home URL
// example: /index.php?r=site/index
echo Url::home();
 
Url::remember(); // save URL to be used later
Url::previous(); // get previously saved URL

There are improvements in URL rules as well. You can use the new yii\web\GroupUrlRule to group rules defining their common parts once instead of repeating them:

new GroupUrlRule([
    'prefix' => 'admin',
    'rules' => [
        'login' => 'user/login',
        'logout' => 'user/logout',
        'dashboard' => 'default/dashboard',
    ],
]);
 
// the above rule is equivalent to the following three rules:
[
    'admin/login' => 'admin/user/login',
    'admin/logout' => 'admin/user/logout',
    'admin/dashboard' => 'admin/default/dashboard',
]

Role-Based Access Control (RBAC)

We have revamped the RBAC implementation by following more closely to the original NIST RBAC model. In particular, we have dropped the concept of operation and task, and replaced them with permission which is the term used in NIST RBAC.

And as aforementioned, we also redesigned the biz rule feature by managing it separately.

Translations

First of all we'd like to thank all the community members who have participated in translating framework messages. The core messages are now available in 26 languages, which is a very impressive number.

Message translation now supports language fallback. For example, if your application is using fr-CA as the language while you only have fr translations, Yii will first look for fr-CA translations; if not found, it will try fr.

A new option is added to every Gii generator, which allows you to choose if you want to generate code with message translated via Yii::t().

The message extraction tool now supports writing strings into .po files as well as databases.

Extensions and Tools

We have built a documentation generator extension named yii2-apidoc that can be used to help you generate nice looking API documentation as well as MarkDown-based tutorials. The generator can be easily customized and extended to fit for your specific needs. It is also used to generate official documentation and API docs, as you can see at http://www.yiiframework.com/doc-2.0/.

The Yii Debugger was polished with many minor enhancements. It is also now equipped with a mail panel as well as DB query and mail summary column in its summary page.

Besides the new translation supported mentioned above, the Yii code generator tool Gii can now be used to create a new extension. You may also notice the code preview window is enhanced so that you can quickly refresh and navigate among different files. It is also copy-paste friendly and supports keyboard shortcuts. Give it a try!

Thank you!

The Yii 2.0 Beta release is a major milestone that has involved tremendous efforts from all parties. We don't think it would be possible without all the valuable contribution from our excellent community. Thank you all for making this release possible.

Tags

release