# Yii 2 Framework Yii 2 is a high-performance PHP framework designed for rapid development of modern web applications. Built on a component-based architecture, it implements the Model-View-Controller (MVC) pattern and provides a comprehensive set of features including Active Record ORM, RESTful API support, caching, authentication, RBAC authorization, database migrations, and internationalization. The framework supports multiple databases including MySQL, PostgreSQL, SQLite, Oracle, and MSSQL. Yii 2 emphasizes code reusability, security, and performance optimization. It features a powerful dependency injection container, event-driven architecture, and extensive helper classes. The framework's modular design allows developers to use only the components they need while maintaining consistent coding standards. Yii 2 requires PHP 7.4+ and works best with PHP 8. ## Active Record - Object-Relational Mapping Active Record provides an object-oriented interface for accessing and manipulating database data. Each Active Record class corresponds to a database table, and each instance represents a row in that table. It supports CRUD operations, relations, eager loading, and complex queries. ```php self::STATUS_ACTIVE], ['status', 'in', 'range' => [self::STATUS_ACTIVE, self::STATUS_INACTIVE]], ]; } // Define relation to orders public function getOrders() { return $this->hasMany(Order::class, ['customer_id' => 'id']); } } // Creating a new record $customer = new Customer(); $customer->name = 'John Doe'; $customer->email = 'john@example.com'; $customer->save(); // INSERT INTO customer ... // Finding records $customer = Customer::findOne(123); // Find by primary key $customer = Customer::findOne(['email' => 'john@example.com']); // Find by condition // Query with conditions $customers = Customer::find() ->where(['status' => Customer::STATUS_ACTIVE]) ->orderBy('name') ->limit(10) ->all(); // Count records $count = Customer::find() ->where(['status' => Customer::STATUS_ACTIVE]) ->count(); // Update a record $customer = Customer::findOne(123); $customer->email = 'newemail@example.com'; $customer->save(); // UPDATE customer SET ... // Delete a record $customer = Customer::findOne(123); $customer->delete(); // DELETE FROM customer WHERE id = 123 // Eager loading relations $customers = Customer::find() ->with('orders') ->all(); foreach ($customers as $customer) { echo $customer->name . ' has ' . count($customer->orders) . ' orders'; } ``` ## Query Builder - Programmatic SQL Construction Query Builder allows constructing SQL queries in a programmatic and database-agnostic way. It automatically handles parameter binding to prevent SQL injection and supports all common SQL operations including SELECT, INSERT, UPDATE, DELETE, JOINs, and subqueries. ```php select(['id', 'email', 'name']) ->from('user') ->where(['status' => 1]) ->orderBy('name') ->limit(10) ->all(); // SELECT with aliases and expressions $query = (new Query()) ->select(['user_id' => 'user.id', 'email', 'full_name' => "CONCAT(first_name, ' ', last_name)"]) ->from('user') ->where(['status' => 1]); // Complex WHERE conditions $query = (new Query()) ->from('user') ->where(['and', ['status' => 1], ['or', ['like', 'name', 'john'], ['like', 'email', 'john'] ], ['between', 'created_at', $startDate, $endDate] ]); // JOIN operations $query = (new Query()) ->select(['user.id', 'user.name', 'profile.bio']) ->from('user') ->leftJoin('profile', 'profile.user_id = user.id') ->where(['user.status' => 1]); // Subqueries $subQuery = (new Query())->select('id')->from('user')->where('status=1'); $query = (new Query()) ->from('order') ->where(['in', 'user_id', $subQuery]); // GROUP BY and aggregates $query = (new Query()) ->select(['user_id', 'total' => 'SUM(amount)']) ->from('order') ->groupBy('user_id') ->having(['>', 'total', 100]); // INSERT data Yii::$app->db->createCommand()->insert('user', [ 'name' => 'John', 'email' => 'john@example.com', ])->execute(); // Batch INSERT Yii::$app->db->createCommand()->batchInsert('user', ['name', 'email'], [ ['John', 'john@example.com'], ['Jane', 'jane@example.com'], ] )->execute(); // UPDATE data Yii::$app->db->createCommand()->update('user', ['status' => 1], ['id' => 123] )->execute(); // DELETE data Yii::$app->db->createCommand()->delete('user', ['status' => 0])->execute(); ``` ## Database Access Objects (DAO) - Raw SQL Execution DAO provides low-level database access for executing raw SQL queries with parameter binding. It offers the most control and best performance for complex operations while maintaining security through prepared statements. ```php [ 'db' => [ 'class' => 'yii\db\Connection', 'dsn' => 'mysql:host=localhost;dbname=mydb', 'username' => 'root', 'password' => 'secret', 'charset' => 'utf8', ], ], ]; // Query all rows $posts = Yii::$app->db->createCommand('SELECT * FROM post WHERE status=:status') ->bindValue(':status', 1) ->queryAll(); // Query single row $post = Yii::$app->db->createCommand('SELECT * FROM post WHERE id=:id') ->bindValue(':id', 123) ->queryOne(); // Query single column $titles = Yii::$app->db->createCommand('SELECT title FROM post') ->queryColumn(); // Query scalar value $count = Yii::$app->db->createCommand('SELECT COUNT(*) FROM post') ->queryScalar(); // Bind multiple parameters $posts = Yii::$app->db->createCommand( 'SELECT * FROM post WHERE status=:status AND author_id=:author', [':status' => 1, ':author' => 5] )->queryAll(); // Execute non-query commands $rowsAffected = Yii::$app->db->createCommand( 'UPDATE post SET status=:status WHERE id=:id', [':status' => 1, ':id' => 123] )->execute(); // Transaction handling $transaction = Yii::$app->db->beginTransaction(); try { Yii::$app->db->createCommand()->insert('order', [ 'customer_id' => 1, 'total' => 100, ])->execute(); Yii::$app->db->createCommand()->insert('order_item', [ 'order_id' => Yii::$app->db->getLastInsertID(), 'product_id' => 5, 'quantity' => 2, ])->execute(); $transaction->commit(); } catch (\Exception $e) { $transaction->rollBack(); throw $e; } // Prepared statement reuse $command = Yii::$app->db->createCommand('SELECT * FROM post WHERE id=:id'); $post1 = $command->bindValue(':id', 1)->queryOne(); $post2 = $command->bindValue(':id', 2)->queryOne(); ``` ## RESTful API Controllers Yii provides built-in support for creating RESTful APIs with automatic CRUD actions, content negotiation, pagination, and authentication. The ActiveController class implements standard REST endpoints with minimal configuration. ```php 'yii\rest\Serializer', 'collectionEnvelope' => 'items', ]; // Configure behaviors (authentication, rate limiting) public function behaviors() { return ArrayHelper::merge(parent::behaviors(), [ 'authenticator' => [ 'class' => \yii\filters\auth\HttpBearerAuth::class, ], 'rateLimiter' => [ 'class' => \yii\filters\RateLimiter::class, 'enableRateLimitHeaders' => true, ], ]); } // Customize actions public function actions() { return ArrayHelper::merge(parent::actions(), [ 'index' => [ 'pagination' => ['pageSize' => 10], 'sort' => ['defaultOrder' => ['created_at' => SORT_DESC]], ], ]); } // Add custom action public function actionSearch($q) { return User::find() ->where(['like', 'name', $q]) ->all(); } } // Configuration: config/web.php return [ 'components' => [ 'urlManager' => [ 'enablePrettyUrl' => true, 'enableStrictParsing' => true, 'showScriptName' => false, 'rules' => [ ['class' => 'yii\rest\UrlRule', 'controller' => 'user'], ], ], 'request' => [ 'parsers' => [ 'application/json' => 'yii\web\JsonParser', ], ], ], ]; // API Usage with curl: // GET /users - List all users // curl -i -H "Accept:application/json" "http://localhost/users" // GET /users/123 - Get user details // curl -i -H "Accept:application/json" "http://localhost/users/123" // POST /users - Create new user // curl -i -H "Accept:application/json" -H "Content-Type:application/json" \ // -XPOST "http://localhost/users" \ // -d '{"username":"john","email":"john@example.com"}' // PUT /users/123 - Update user // curl -i -H "Accept:application/json" -H "Content-Type:application/json" \ // -XPUT "http://localhost/users/123" \ // -d '{"email":"newemail@example.com"}' // DELETE /users/123 - Delete user // curl -i -XDELETE "http://localhost/users/123" ``` ## Controllers and Actions - Request Handling Controllers process incoming requests and generate responses. They contain actions that are the basic units users can address. Yii supports both inline actions (controller methods) and standalone action classes. ```php [ 'class' => AccessControl::class, 'rules' => [ ['allow' => true, 'actions' => ['index', 'view'], 'roles' => ['?', '@']], ['allow' => true, 'actions' => ['create', 'update', 'delete'], 'roles' => ['@']], ], ], 'verbs' => [ 'class' => VerbFilter::class, 'actions' => [ 'delete' => ['POST'], ], ], ]; } // List all posts public function actionIndex() { $posts = Post::find() ->where(['status' => Post::STATUS_PUBLISHED]) ->orderBy('created_at DESC') ->all(); return $this->render('index', ['posts' => $posts]); } // View single post with parameter binding public function actionView($id) { $post = Post::findOne($id); if ($post === null) { throw new NotFoundHttpException('Post not found.'); } return $this->render('view', ['model' => $post]); } // Create post with form handling public function actionCreate() { $model = new Post(); if ($model->load(Yii::$app->request->post()) && $model->save()) { Yii::$app->session->setFlash('success', 'Post created successfully.'); return $this->redirect(['view', 'id' => $model->id]); } return $this->render('create', ['model' => $model]); } // Update post public function actionUpdate($id) { $model = Post::findOne($id); if ($model === null) { throw new NotFoundHttpException('Post not found.'); } if ($model->load(Yii::$app->request->post()) && $model->save()) { return $this->redirect(['view', 'id' => $model->id]); } return $this->render('update', ['model' => $model]); } // Delete post public function actionDelete($id) { $model = Post::findOne($id); if ($model !== null) { $model->delete(); } return $this->redirect(['index']); } // JSON response for AJAX public function actionGetPostData($id) { Yii::$app->response->format = \yii\web\Response::FORMAT_JSON; $post = Post::findOne($id); if ($post === null) { return ['success' => false, 'error' => 'Not found']; } return ['success' => true, 'data' => $post->toArray()]; } } ``` ## Model Validation - Input Validation Rules Models in Yii provide built-in validation support through declarative rules. Validators can be core validators, inline validators, or standalone validator classes. Validation integrates with forms and provides both server-side and client-side validation. ```php 100], ['subject', 'string', 'min' => 10, 'max' => 200], ['body', 'string', 'min' => 20], // Custom error message ['name', 'required', 'message' => 'Please enter your name.'], // Regular expression ['phone', 'match', 'pattern' => '/^\+?[0-9]{10,14}$/'], // In range ['country', 'in', 'range' => ['USA', 'Canada', 'UK', 'Germany']], // Conditional validation ['state', 'required', 'when' => function($model) { return $model->country === 'USA'; }, 'whenClient' => "function(attribute, value) { return $('#country').val() === 'USA'; }"], // Default value ['country', 'default', 'value' => 'USA'], // Trim whitespace [['name', 'email', 'subject'], 'trim'], // Safe attribute (mass assignment allowed) ['phone', 'safe'], ]; } // Custom inline validator public function validateBody($attribute, $params) { if (stripos($this->$attribute, 'spam') !== false) { $this->addError($attribute, 'Message appears to contain spam.'); } } public function attributeLabels() { return [ 'name' => 'Your Name', 'email' => 'Email Address', 'subject' => 'Subject', 'body' => 'Message', ]; } } // Usage in controller $model = new ContactForm(); $model->load(Yii::$app->request->post()); if ($model->validate()) { // All inputs are valid $this->sendEmail($model); return $this->redirect(['site/thanks']); } else { // Validation failed $errors = $model->errors; // ['email' => ['Email is invalid.']] $firstErrors = $model->firstErrors; // ['email' => 'Email is invalid.'] } // DynamicModel for ad-hoc validation $model = \yii\base\DynamicModel::validateData( ['email' => 'test@example.com', 'name' => 'John'], [ [['email', 'name'], 'required'], ['email', 'email'], ] ); if ($model->hasErrors()) { // Handle validation errors } ``` ## Data Caching - Performance Optimization Yii provides a unified caching API supporting multiple backends including APC, Memcached, Redis, file-based, and database caching. Caching integrates with query results, fragments, pages, and HTTP responses. ```php [ // File cache (default) 'cache' => [ 'class' => 'yii\caching\FileCache', 'cachePath' => '@runtime/cache', ], // Or Memcached 'cache' => [ 'class' => 'yii\caching\MemCache', 'servers' => [ ['host' => 'server1', 'port' => 11211, 'weight' => 100], ['host' => 'server2', 'port' => 11211, 'weight' => 50], ], ], // Or Redis 'cache' => [ 'class' => 'yii\redis\Cache', 'redis' => [ 'hostname' => 'localhost', 'port' => 6379, 'database' => 0, ], ], ], ]; // Basic cache operations $cache = Yii::$app->cache; // Get or set pattern $data = $cache->get('my_data'); if ($data === false) { $data = $this->calculateExpensiveData(); $cache->set('my_data', $data, 3600); // Cache for 1 hour } // Simplified getOrSet (since 2.0.11) $data = $cache->getOrSet('my_data', function() { return $this->calculateExpensiveData(); }, 3600); // With dependencies use yii\caching\DbDependency; $dependency = new DbDependency([ 'sql' => 'SELECT MAX(updated_at) FROM post', ]); $data = $cache->getOrSet('posts', function() { return Post::find()->all(); }, 3600, $dependency); // Tag-based cache invalidation use yii\caching\TagDependency; $cache->set('post_1', $post1, 0, new TagDependency(['tags' => ['posts', 'post_1']])); $cache->set('post_2', $post2, 0, new TagDependency(['tags' => ['posts', 'post_2']])); // Invalidate all posts TagDependency::invalidate($cache, 'posts'); // Query caching in Active Record $posts = Post::find() ->cache(3600, new DbDependency(['sql' => 'SELECT MAX(updated_at) FROM post'])) ->all(); // Or using connection-level caching Yii::$app->db->cache(function($db) { return Post::find()->all(); }, 3600); // Array access syntax $cache['user_123'] = $userData; $userData = $cache['user_123']; // Delete and flush $cache->delete('my_data'); $cache->flush(); // Clear all cache ``` ## Authentication - User Identity Management Yii's authentication framework manages user login states, sessions, and identity verification. It requires implementing the IdentityInterface on your User model and configuring the user application component. ```php $token]); } // Find user by username (for login form) public static function findByUsername($username) { return static::findOne(['username' => $username]); } public function getId() { return $this->id; } public function getAuthKey() { return $this->auth_key; } public function validateAuthKey($authKey) { return $this->auth_key === $authKey; } // Validate password public function validatePassword($password) { return Yii::$app->security->validatePassword($password, $this->password_hash); } // Set password public function setPassword($password) { $this->password_hash = Yii::$app->security->generatePasswordHash($password); } // Generate auth key public function generateAuthKey() { $this->auth_key = Yii::$app->security->generateRandomString(); } public function beforeSave($insert) { if (parent::beforeSave($insert)) { if ($this->isNewRecord) { $this->generateAuthKey(); } return true; } return false; } } // Login form model class LoginForm extends \yii\base\Model { public $username; public $password; public $rememberMe = true; public function rules() { return [ [['username', 'password'], 'required'], ['rememberMe', 'boolean'], ['password', 'validatePassword'], ]; } public function validatePassword($attribute) { if (!$this->hasErrors()) { $user = User::findByUsername($this->username); if (!$user || !$user->validatePassword($this->password)) { $this->addError($attribute, 'Incorrect username or password.'); } } } public function login() { if ($this->validate()) { $user = User::findByUsername($this->username); return Yii::$app->user->login($user, $this->rememberMe ? 3600*24*30 : 0); } return false; } } // Using authentication in controllers class SiteController extends \yii\web\Controller { public function actionLogin() { $model = new LoginForm(); if ($model->load(Yii::$app->request->post()) && $model->login()) { return $this->goBack(); } return $this->render('login', ['model' => $model]); } public function actionLogout() { Yii::$app->user->logout(); return $this->goHome(); } public function actionProfile() { // Get current user identity $identity = Yii::$app->user->identity; $userId = Yii::$app->user->id; $isGuest = Yii::$app->user->isGuest; if ($isGuest) { return $this->redirect(['site/login']); } return $this->render('profile', ['user' => $identity]); } } ``` ## Database Migrations - Schema Version Control Migrations allow version-controlled database schema changes. They support creating tables, adding columns, creating indexes, and seeding data. Migrations can be applied, reverted, and re-applied using console commands. ```php createTable('user', [ 'id' => $this->primaryKey(), 'username' => $this->string(255)->notNull()->unique(), 'email' => $this->string(255)->notNull()->unique(), 'password_hash' => $this->string(255)->notNull(), 'auth_key' => $this->string(32)->notNull(), 'status' => $this->smallInteger()->notNull()->defaultValue(1), 'created_at' => $this->integer()->notNull(), 'updated_at' => $this->integer()->notNull(), ]); // Create index $this->createIndex('idx-user-status', 'user', 'status'); // Insert default data $this->insert('user', [ 'username' => 'admin', 'email' => 'admin@example.com', 'password_hash' => Yii::$app->security->generatePasswordHash('admin123'), 'auth_key' => Yii::$app->security->generateRandomString(), 'status' => 1, 'created_at' => time(), 'updated_at' => time(), ]); } public function safeDown() { $this->dropTable('user'); } } // Create migration: yii migrate/create add_profile_to_user class m230102_100000_add_profile_to_user extends Migration { public function safeUp() { $this->addColumn('user', 'profile_image', $this->string(255)->null()); $this->addColumn('user', 'bio', $this->text()->null()); } public function safeDown() { $this->dropColumn('user', 'profile_image'); $this->dropColumn('user', 'bio'); } } // Create migration with foreign key class m230103_100000_create_post_table extends Migration { public function safeUp() { $this->createTable('post', [ 'id' => $this->primaryKey(), 'user_id' => $this->integer()->notNull(), 'title' => $this->string(255)->notNull(), 'content' => $this->text(), 'status' => $this->smallInteger()->defaultValue(0), 'created_at' => $this->integer()->notNull(), ]); // Add foreign key $this->addForeignKey( 'fk-post-user_id', 'post', 'user_id', 'user', 'id', 'CASCADE', 'CASCADE' ); } public function safeDown() { $this->dropForeignKey('fk-post-user_id', 'post'); $this->dropTable('post'); } } // Console commands: // yii migrate - Apply all new migrations // yii migrate/up 3 - Apply next 3 migrations // yii migrate/down 1 - Revert last migration // yii migrate/redo - Revert and re-apply last migration // yii migrate/history - Show migration history // yii migrate/new - Show pending migrations // yii migrate/fresh - Drop all tables and re-run all migrations ``` ## Console Commands - CLI Applications Yii console applications provide command-line tools for tasks like migrations, cron jobs, and data processing. Custom commands are created by extending the console Controller class and defining action methods. ```php 'message']; } // Action: yii hello public function actionIndex($name = 'World') { echo "{$this->message}, {$name}!\n"; return ExitCode::OK; } // Action with colored output: yii hello/colorful public function actionColorful() { $this->stdout("Success message\n", Console::FG_GREEN); $this->stdout("Warning message\n", Console::FG_YELLOW); $this->stderr("Error message\n", Console::FG_RED); return ExitCode::OK; } // Interactive action: yii hello/interactive public function actionInteractive() { $name = $this->prompt('What is your name?', ['default' => 'Guest']); if ($this->confirm("Do you want to proceed, {$name}?")) { $this->stdout("Processing...\n"); // Progress bar Console::startProgress(0, 100); for ($i = 0; $i <= 100; $i++) { Console::updateProgress($i, 100); usleep(20000); } Console::endProgress(); $this->stdout("Done!\n", Console::FG_GREEN); } return ExitCode::OK; } // Select from options public function actionSelect() { $choice = $this->select('Choose color:', [ 'r' => 'Red', 'g' => 'Green', 'b' => 'Blue', ]); echo "You selected: {$choice}\n"; return ExitCode::OK; } } // Data processing command class DataController extends Controller { // yii data/import --file=data.csv public $file; public function options($actionID) { return ['file']; } public function actionImport() { if (!$this->file || !file_exists($this->file)) { $this->stderr("File not found: {$this->file}\n", Console::FG_RED); return ExitCode::UNSPECIFIED_ERROR; } $handle = fopen($this->file, 'r'); $count = 0; while (($data = fgetcsv($handle)) !== false) { // Process each row $count++; } fclose($handle); $this->stdout("Imported {$count} records.\n", Console::FG_GREEN); return ExitCode::OK; } // Scheduled task for cron public function actionCleanup() { $deleted = \app\models\Session::deleteAll([ '<', 'expire', time() - 86400 ]); echo "Deleted {$deleted} expired sessions.\n"; return ExitCode::OK; } } // Run commands: // ./yii hello - Run default action // ./yii hello/index John - With argument // ./yii hello -m "Hi" - With option // ./yii data/import --file=a.csv - Import data ``` ## Summary Yii 2 is designed for building web applications ranging from simple websites to complex enterprise systems and RESTful APIs. The framework excels in rapid application development through its powerful code generation tools (Gii), comprehensive Active Record implementation, and extensive widget library. Common use cases include e-commerce platforms, content management systems, RESTful API backends, admin panels, and SaaS applications. The framework's modular architecture allows developers to use components independently, making it suitable for both greenfield projects and integration with existing systems. Integration patterns in Yii 2 follow a component-based approach where services are configured as application components and accessed through the service locator (`Yii::$app`). The dependency injection container enables loose coupling between classes, while events and behaviors provide extensibility without modifying core code. For RESTful APIs, the framework provides ActiveController for quick CRUD implementations and supports authentication methods like HTTP Basic, Bearer tokens, and OAuth2. Database operations can be performed at multiple abstraction levels: DAO for raw SQL, Query Builder for programmatic queries, and Active Record for object-relational mapping, allowing developers to choose the appropriate level based on performance and complexity requirements.