From 7432d4bfceac628bb0f7cfe47414792cf850434e Mon Sep 17 00:00:00 2001 From: Nobuo Kihara Date: Thu, 4 Dec 2014 22:35:04 +0900 Subject: [PATCH 1/7] docs/guide-ja/README.md - updated [ci skip] --- docs/guide-ja/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide-ja/README.md b/docs/guide-ja/README.md index c9157e8..4d6c53e 100644 --- a/docs/guide-ja/README.md +++ b/docs/guide-ja/README.md @@ -193,7 +193,7 @@ RESTful ウェブサービス * [概要](helper-overview.md) * [ArrayHelper](helper-array.md) -* **TBD** [Html](helper-html.md) +* [Html](helper-html.md) * [Url](helper-url.md) * **TBD** [Security](helper-security.md) From 49f0cbef37b27a48c0df769defdb656f09605b6a Mon Sep 17 00:00:00 2001 From: Nobuo Kihara Date: Fri, 5 Dec 2014 18:33:49 +0900 Subject: [PATCH 2/7] docs/guide-ja/db-active-record.md - started translation --- docs/guide-ja/db-active-record.md | 1037 +++++++++++++++++++++++++++++++++++++ 1 file changed, 1037 insertions(+) create mode 100644 docs/guide-ja/db-active-record.md diff --git a/docs/guide-ja/db-active-record.md b/docs/guide-ja/db-active-record.md new file mode 100644 index 0000000..18b0a8c --- /dev/null +++ b/docs/guide-ja/db-active-record.md @@ -0,0 +1,1037 @@ +Active Record +============= + +> Note: This section is under development. + +[Active Record](http://en.wikipedia.org/wiki/Active_record_pattern) provides an object-oriented interface +for accessing data stored in a database. An Active Record class is associated with a database table, +an Active Record instance corresponds to a row of that table, and an attribute of an Active Record +instance represents the value of a column in that row. Instead of writing raw SQL statements, +you can work with Active Record in an object-oriented fashion to manipulate the data in database tables. + +For example, assume `Customer` is an Active Record class is associated with the `customer` table +and `name` is a column of the `customer` table. You can write the following code to insert a new +row into the `customer` table: + +```php +$customer = new Customer(); +$customer->name = 'Qiang'; +$customer->save(); +``` + +The above code is equivalent to using the following raw SQL statement, which is less +intuitive, more error prone, and may have compatibility problems for different DBMS: + +```php +$db->createCommand('INSERT INTO customer (name) VALUES (:name)', [ + ':name' => 'Qiang', +])->execute(); +``` + +Below is the list of databases that are currently supported by Yii Active Record: + +* MySQL 4.1 or later: via [[yii\db\ActiveRecord]] +* PostgreSQL 7.3 or later: via [[yii\db\ActiveRecord]] +* SQLite 2 and 3: via [[yii\db\ActiveRecord]] +* Microsoft SQL Server 2010 or later: via [[yii\db\ActiveRecord]] +* Oracle: via [[yii\db\ActiveRecord]] +* CUBRID 9.3 or later: via [[yii\db\ActiveRecord]] (Note that due to a [bug](http://jira.cubrid.org/browse/APIS-658) in + the cubrid PDO extension, quoting of values will not work, so you need CUBRID 9.3 as the client as well as the server) +* Sphnix: via [[yii\sphinx\ActiveRecord]], requires the `yii2-sphinx` extension +* ElasticSearch: via [[yii\elasticsearch\ActiveRecord]], requires the `yii2-elasticsearch` extension +* Redis 2.6.12 or later: via [[yii\redis\ActiveRecord]], requires the `yii2-redis` extension +* MongoDB 1.3.0 or later: via [[yii\mongodb\ActiveRecord]], requires the `yii2-mongodb` extension + +As you can see, Yii provides Active Record support for relational databases as well as NoSQL databases. +In this tutorial, we will mainly describe the usage of Active Record for relational databases. +However, most content described here are also applicable to Active Record for NoSQL databases. + + +Declaring Active Record Classes +------------------------------ + +To declare an Active Record class you need to extend [[yii\db\ActiveRecord]] and implement +the `tableName` method that returns the name of the database table associated with the class: + +```php +namespace app\models; + +use yii\db\ActiveRecord; + +class Customer extends ActiveRecord +{ + const STATUS_ACTIVE = 'active'; + const STATUS_DELETED = 'deleted'; + + /** + * @return string the name of the table associated with this ActiveRecord class. + */ + public static function tableName() + { + return 'customer'; + } +} +``` + + +Accessing Column Data +--------------------- + +Active Record maps each column of the corresponding database table row to an attribute in the Active Record +object. An attribute behaves like a regular object public property. The name of an attribute is the same +as the corresponding column name and is case-sensitive. + +To read the value of a column, you can use the following syntax: + +```php +// "id" and "email" are the names of columns in the table associated with the $customer ActiveRecord object +$id = $customer->id; +$email = $customer->email; +``` + +To change the value of a column, assign a new value to the associated property and save the object: + +```php +$customer->email = 'jane@example.com'; +$customer->save(); +``` + + +Connecting to Database +---------------------- + +Active Record uses a [[yii\db\Connection|DB connection]] to exchange data with the database. By default, +it uses the `db` [application component](structure-application-components.md) as the connection. As explained in [Database basics](db-dao.md), +you may configure the `db` component in the application configuration file as follows, + +```php +return [ + 'components' => [ + 'db' => [ + 'class' => 'yii\db\Connection', + 'dsn' => 'mysql:host=localhost;dbname=testdb', + 'username' => 'demo', + 'password' => 'demo', + ], + ], +]; +``` + +If you are using multiple databases in your application and you want to use a different DB connection +for your Active Record class, you may override the [[yii\db\ActiveRecord::getDb()|getDb()]] method: + +```php +class Customer extends ActiveRecord +{ + // ... + + public static function getDb() + { + return \Yii::$app->db2; // use the "db2" application component + } +} +``` + + +Querying Data from Database +--------------------------- + +Active Record provides two entry methods for building DB queries and populating data into Active Record instances: + + - [[yii\db\ActiveRecord::find()]] + - [[yii\db\ActiveRecord::findBySql()]] + +Both methods return an [[yii\db\ActiveQuery]] instance, which extends [[yii\db\Query]], and thus supports the same set +of flexible and powerful DB query building methods, such as `where()`, `join()`, `orderBy()`, etc. The following examples +demonstrate some of the possibilities. + +```php +// to retrieve all *active* customers and order them by their ID: +$customers = Customer::find() + ->where(['status' => Customer::STATUS_ACTIVE]) + ->orderBy('id') + ->all(); + +// to return a single customer whose ID is 1: +$customer = Customer::find() + ->where(['id' => 1]) + ->one(); + +// to return the number of *active* customers: +$count = Customer::find() + ->where(['status' => Customer::STATUS_ACTIVE]) + ->count(); + +// to index the result by customer IDs: +$customers = Customer::find()->indexBy('id')->all(); +// $customers array is indexed by customer IDs + +// to retrieve customers using a raw SQL statement: +$sql = 'SELECT * FROM customer'; +$customers = Customer::findBySql($sql)->all(); +``` + +> Tip: In the code above `Customer::STATUS_ACTIVE` is a constant defined in `Customer`. It is a good practice to + use meaningful constant names rather than hardcoded strings or numbers in your code. + + +Two shortcut methods are provided to return Active Record instances matching a primary key value or a set of +column values: `findOne()` and `findAll()`. The former returns the first matching instance while the latter +returns all of them. For example, + +```php +// to return a single customer whose ID is 1: +$customer = Customer::findOne(1); + +// to return an *active* customer whose ID is 1: +$customer = Customer::findOne([ + 'id' => 1, + 'status' => Customer::STATUS_ACTIVE, +]); + +// to return customers whose ID is 1, 2 or 3: +$customers = Customer::findAll([1, 2, 3]); + +// to return customers whose status is "deleted": +$customer = Customer::findAll([ + 'status' => Customer::STATUS_DELETED, +]); +``` + +> Note: By default neither `findOne()` nor `one()` will add `LIMIT 1` to the query. This is fine and preferred + if you know the query will return only one or a few rows of data (e.g. if you are querying with some primary keys). + However, if the query may potentially return many rows of data, you should call `limit(1)` to improve the performance. + For example, `Customer::find()->where(['status' => Customer::STATUS_ACTIVE])->limit(1)->one()`. + + +### Retrieving Data in Arrays + +Sometimes when you are processing a large amount of data, you may want to use arrays to hold the data +retrieved from database to save memory. This can be done by calling `asArray()`: + +```php +// to return customers in terms of arrays rather than `Customer` objects: +$customers = Customer::find() + ->asArray() + ->all(); +// each element of $customers is an array of name-value pairs +``` + +Note that while this method saves memory and improves performance it is a step to a lower abstraction +layer and you will loose some features that the active record layer has. +Fetching data using asArray is nearly equal to running a normal query using the [query builder](db-dao.md). +When using asArray the result will be returned as a simple array with no typecasting performed +so the result may contain string values for fields that are integer when accessed on the active record object. + +### Retrieving Data in Batches + +In [Query Builder](db-query-builder.md), we have explained that you may use *batch query* to minimize your memory +usage when querying a large amount of data from the database. You may use the same technique +in Active Record. For example, + +```php +// 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) { +} +``` + + +Manipulating Data in Database +----------------------------- + +Active Record provides the following methods to insert, update and delete a single row in a table associated with +a single Active Record instance: + +- [[yii\db\ActiveRecord::save()|save()]] +- [[yii\db\ActiveRecord::insert()|insert()]] +- [[yii\db\ActiveRecord::update()|update()]] +- [[yii\db\ActiveRecord::delete()|delete()]] + +Active Record also provides the following static methods that apply to a whole table associated with +an Active Record class. Be extremely careful when using these methods as they affect the whole table. +For example, `deleteAll()` will delete ALL rows in the table. + +- [[yii\db\ActiveRecord::updateCounters()|updateCounters()]] +- [[yii\db\ActiveRecord::updateAll()|updateAll()]] +- [[yii\db\ActiveRecord::updateAllCounters()|updateAllCounters()]] +- [[yii\db\ActiveRecord::deleteAll()|deleteAll()]] + + +The following examples show how to use these methods: + +```php +// to insert a new customer record +$customer = new Customer(); +$customer->name = 'James'; +$customer->email = 'james@example.com'; +$customer->save(); // equivalent to $customer->insert(); + +// to update an existing customer record +$customer = Customer::findOne($id); +$customer->email = 'james@example.com'; +$customer->save(); // equivalent to $customer->update(); + +// to delete an existing customer record +$customer = Customer::findOne($id); +$customer->delete(); + +// to delete several customers +Customer::deleteAll('age > :age AND gender = :gender', [':age' => 20, ':gender' => 'M']); + +// to increment the age of ALL customers by 1 +Customer::updateAllCounters(['age' => 1]); +``` + +> Info: The `save()` method will call either `insert()` or `update()`, depending on whether + the Active Record instance is new or not (internally it will check the value of [[yii\db\ActiveRecord::isNewRecord]]). + If an Active Record is instantiated via the `new` operator, calling `save()` will + insert a row in the table; calling `save()` on an active record fetched from the database will update the corresponding + row in the table. + + +### Data Input and Validation + +Because Active Record extends from [[yii\base\Model]], it supports the same data input and validation features +as described in [Model](structure-models.md). For example, you may declare validation rules by overwriting the +[[yii\base\Model::rules()|rules()]] method; you may massively assign user input data to an Active Record instance; +and you may call [[yii\base\Model::validate()|validate()]] to trigger data validation. + +When you call `save()`, `insert()` or `update()`, these methods will automatically call [[yii\base\Model::validate()|validate()]]. +If the validation fails, the corresponding data saving operation will be cancelled. + +The following example shows how to use an Active Record to collect/validate user input and save them into the database: + +```php +// creating a new record +$model = new Customer; +if ($model->load(Yii::$app->request->post()) && $model->save()) { + // the user input has been collected, validated and saved +} + +// updating a record whose primary key is $id +$model = Customer::findOne($id); +if ($model === null) { + throw new NotFoundHttpException; +} +if ($model->load(Yii::$app->request->post()) && $model->save()) { + // the user input has been collected, validated and saved +} +``` + + +### Loading Default Values + +Your table columns may be defined with default values. Sometimes, you may want to pre-populate your +Web form for an Active Record with these values. To do so, call the +[[yii\db\ActiveRecord::loadDefaultValues()|loadDefaultValues()]] method before rendering the form: + +```php +$customer = new Customer(); +$customer->loadDefaultValues(); +// ... render HTML form for $customer ... +``` + +If you want to set some initial values for the attributes yourself you can override the `init()` method +of the active record class and set the values there. For example to set the default value for the `status` attribute: + +```php +public function init() +{ + parent::init(); + $this->status = 'active'; +} +``` + +Active Record Life Cycles +------------------------- + +It is important to understand the life cycles of Active Record when it is used to manipulate data in database. +These life cycles are typically associated with corresponding events which allow you to inject code +to intercept or respond to these events. They are especially useful for developing Active Record [behaviors](concept-behaviors.md). + +When instantiating a new Active Record instance, we will have the following life cycles: + +1. constructor +2. [[yii\db\ActiveRecord::init()|init()]]: will trigger an [[yii\db\ActiveRecord::EVENT_INIT|EVENT_INIT]] event + +When querying data through the [[yii\db\ActiveRecord::find()|find()]] method, we will have the following life cycles +for EVERY newly populated Active Record instance: + +1. constructor +2. [[yii\db\ActiveRecord::init()|init()]]: will trigger an [[yii\db\ActiveRecord::EVENT_INIT|EVENT_INIT]] event +3. [[yii\db\ActiveRecord::afterFind()|afterFind()]]: will trigger an [[yii\db\ActiveRecord::EVENT_AFTER_FIND|EVENT_AFTER_FIND]] event + +When calling [[yii\db\ActiveRecord::save()|save()]] to insert or update an ActiveRecord, we will have +the following life cycles: + +1. [[yii\db\ActiveRecord::beforeValidate()|beforeValidate()]]: will trigger an [[yii\db\ActiveRecord::EVENT_BEFORE_VALIDATE|EVENT_BEFORE_VALIDATE]] event +2. [[yii\db\ActiveRecord::afterValidate()|afterValidate()]]: will trigger an [[yii\db\ActiveRecord::EVENT_AFTER_VALIDATE|EVENT_AFTER_VALIDATE]] event +3. [[yii\db\ActiveRecord::beforeSave()|beforeSave()]]: will trigger an [[yii\db\ActiveRecord::EVENT_BEFORE_INSERT|EVENT_BEFORE_INSERT]] or [[yii\db\ActiveRecord::EVENT_BEFORE_UPDATE|EVENT_BEFORE_UPDATE]] event +4. perform the actual data insertion or updating +5. [[yii\db\ActiveRecord::afterSave()|afterSave()]]: will trigger an [[yii\db\ActiveRecord::EVENT_AFTER_INSERT|EVENT_AFTER_INSERT]] or [[yii\db\ActiveRecord::EVENT_AFTER_UPDATE|EVENT_AFTER_UPDATE]] event + +And finally, when calling [[yii\db\ActiveRecord::delete()|delete()]] to delete an ActiveRecord, we will have +the following life cycles: + +1. [[yii\db\ActiveRecord::beforeDelete()|beforeDelete()]]: will trigger an [[yii\db\ActiveRecord::EVENT_BEFORE_DELETE|EVENT_BEFORE_DELETE]] event +2. perform the actual data deletion +3. [[yii\db\ActiveRecord::afterDelete()|afterDelete()]]: will trigger an [[yii\db\ActiveRecord::EVENT_AFTER_DELETE|EVENT_AFTER_DELETE]] event + + +Working with Relational Data +---------------------------- + +You can use ActiveRecord to also query a table's relational data (i.e., selection of data from Table A can also pull +in related data from Table B). Thanks to ActiveRecord, the relational data returned can be accessed like a property +of the ActiveRecord object associated with the primary table. + +For example, with an appropriate relation declaration, by accessing `$customer->orders` you may obtain +an array of `Order` objects which represent the orders placed by the specified customer. + +To declare a relation, define a getter method which returns an [[yii\db\ActiveQuery]] object that has relation +information about the relation context and thus will only query for related records. For example, + +```php +class Customer extends \yii\db\ActiveRecord +{ + public function getOrders() + { + // Customer has_many Order via Order.customer_id -> id + return $this->hasMany(Order::className(), ['customer_id' => 'id']); + } +} + +class Order extends \yii\db\ActiveRecord +{ + public function getCustomer() + { + // Order has_one Customer via Customer.id -> customer_id + return $this->hasOne(Customer::className(), ['id' => 'customer_id']); + } +} +``` + +The methods [[yii\db\ActiveRecord::hasMany()]] and [[yii\db\ActiveRecord::hasOne()]] used in the above +are used to model the many-one relationship and one-one relationship in a relational database. +For example, a customer has many orders, and an order has one customer. +Both methods take two parameters and return an [[yii\db\ActiveQuery]] object: + + - `$class`: the name of the class of the related model(s). This should be a fully qualified class name. + - `$link`: the association between columns from the two tables. This should be given as an array. + The keys of the array are the names of the columns from the table associated with `$class`, + while the values of the array are the names of the columns from the declaring class. + It is a good practice to define relationships based on table foreign keys. + +After declaring relations, getting relational data is as easy as accessing a component property +that is defined by the corresponding getter method: + +```php +// get the orders of a customer +$customer = Customer::findOne(1); +$orders = $customer->orders; // $orders is an array of Order objects +``` + +Behind the scenes, the above code executes the following two SQL queries, one for each line of code: + +```sql +SELECT * FROM customer WHERE id=1; +SELECT * FROM order WHERE customer_id=1; +``` + +> Tip: If you access the expression `$customer->orders` again, it will not perform the second SQL query again. +The SQL query is only performed the first time when this expression is accessed. Any further +accesses will only return the previously fetched results that are cached internally. If you want to re-query +the relational data, simply unset the existing expression first: `unset($customer->orders);`. + +Sometimes, you may want to pass parameters to a relational query. For example, instead of returning +all orders of a customer, you may want to return only big orders whose subtotal exceeds a specified amount. +To do so, declare a `bigOrders` relation with the following getter method: + +```php +class Customer extends \yii\db\ActiveRecord +{ + public function getBigOrders($threshold = 100) + { + return $this->hasMany(Order::className(), ['customer_id' => 'id']) + ->where('subtotal > :threshold', [':threshold' => $threshold]) + ->orderBy('id'); + } +} +``` + +Remember that `hasMany()` returns an [[yii\db\ActiveQuery]] object which allows you to customize the query by +calling the methods of [[yii\db\ActiveQuery]]. + +With the above declaration, if you access `$customer->bigOrders`, it will only return the orders +whose subtotal is greater than 100. To specify a different threshold value, use the following code: + +```php +$orders = $customer->getBigOrders(200)->all(); +``` + +> Note: A relation method returns an instance of [[yii\db\ActiveQuery]]. If you access the relation like +an attribute (i.e. a class property), the return value will be the query result of the relation, which could be an instance of [[yii\db\ActiveRecord]], +an array of that, or null, depending on the multiplicity of the relation. For example, `$customer->getOrders()` returns +an `ActiveQuery` instance, while `$customer->orders` returns an array of `Order` objects (or an empty array if +the query results in nothing). + + +Relations with Junction Table +----------------------------- + +Sometimes, two tables are related together via an intermediary table called a [junction table][]. To declare such relations, +we can customize the [[yii\db\ActiveQuery]] object by calling its [[yii\db\ActiveQuery::via()|via()]] or +[[yii\db\ActiveQuery::viaTable()|viaTable()]] method. + +For example, if table `order` and table `item` are related via the junction table `order_item`, +we can declare the `items` relation in the `Order` class like the following: + +```php +class Order extends \yii\db\ActiveRecord +{ + public function getItems() + { + return $this->hasMany(Item::className(), ['id' => 'item_id']) + ->viaTable('order_item', ['order_id' => 'id']); + } +} +``` + +The [[yii\db\ActiveQuery::via()|via()]] method is similar to [[yii\db\ActiveQuery::viaTable()|viaTable()]] except that +the first parameter of [[yii\db\ActiveQuery::via()|via()]] takes a relation name declared in the ActiveRecord class +instead of the junction table name. For example, the above `items` relation can be equivalently declared as follows: + +```php +class Order extends \yii\db\ActiveRecord +{ + public function getOrderItems() + { + return $this->hasMany(OrderItem::className(), ['order_id' => 'id']); + } + + public function getItems() + { + return $this->hasMany(Item::className(), ['id' => 'item_id']) + ->via('orderItems'); + } +} +``` + +[junction table]: https://en.wikipedia.org/wiki/Junction_table "Junction table on Wikipedia" + + +Lazy and Eager Loading +---------------------- + +As described earlier, when you access the related objects for the first time, ActiveRecord will perform a DB query +to retrieve the corresponding data and populate it into the related objects. No query will be performed +if you access the same related objects again. We call this *lazy loading*. For example, + +```php +// SQL executed: SELECT * FROM customer WHERE id=1 +$customer = Customer::findOne(1); +// SQL executed: SELECT * FROM order WHERE customer_id=1 +$orders = $customer->orders; +// no SQL executed +$orders2 = $customer->orders; +``` + +Lazy loading is very convenient to use. However, it may suffer from a performance issue in the following scenario: + +```php +// SQL executed: SELECT * FROM customer LIMIT 100 +$customers = Customer::find()->limit(100)->all(); + +foreach ($customers as $customer) { + // SQL executed: SELECT * FROM order WHERE customer_id=... + $orders = $customer->orders; + // ...handle $orders... +} +``` + +How many SQL queries will be performed in the above code, assuming there are more than 100 customers in +the database? 101! The first SQL query brings back 100 customers. Then for each customer, a SQL query +is performed to bring back the orders of that customer. + +To solve the above performance problem, you can use the so-called *eager loading* approach by calling [[yii\db\ActiveQuery::with()]]: + +```php +// SQL executed: SELECT * FROM customer LIMIT 100; +// SELECT * FROM orders WHERE customer_id IN (1,2,...) +$customers = Customer::find()->limit(100) + ->with('orders')->all(); + +foreach ($customers as $customer) { + // no SQL executed + $orders = $customer->orders; + // ...handle $orders... +} +``` + +As you can see, only two SQL queries are needed for the same task! + +> Info: In general, if you are eager loading `N` relations among which `M` relations are defined with `via()` or `viaTable()`, +> a total number of `1+M+N` SQL queries will be performed: one query to bring back the rows for the primary table, one for +> each of the `M` junction tables corresponding to the `via()` or `viaTable()` calls, and one for each of the `N` related tables. + +> Note: When you are customizing `select()` with eager loading, make sure you include the columns that link +> the related models. Otherwise, the related models will not be loaded. For example, + +```php +$orders = Order::find()->select(['id', 'amount'])->with('customer')->all(); +// $orders[0]->customer is always null. To fix the problem, you should do the following: +$orders = Order::find()->select(['id', 'amount', 'customer_id'])->with('customer')->all(); +``` + +Sometimes, you may want to customize the relational queries on the fly. This can be +done for both lazy loading and eager loading. For example, + +```php +$customer = Customer::findOne(1); +// lazy loading: SELECT * FROM order WHERE customer_id=1 AND subtotal>100 +$orders = $customer->getOrders()->where('subtotal>100')->all(); + +// eager loading: SELECT * FROM customer LIMIT 100 +// SELECT * FROM order WHERE customer_id IN (1,2,...) AND subtotal>100 +$customers = Customer::find()->limit(100)->with([ + 'orders' => function($query) { + $query->andWhere('subtotal>100'); + }, +])->all(); +``` + + +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`: + +```php +class Customer extends ActiveRecord +{ + .... + public function getOrders() + { + return $this->hasMany(Order::className(), ['customer_id' => 'id']); + } +} + +class Order extends ActiveRecord +{ + .... + public function getCustomer() + { + return $this->hasOne(Customer::className(), ['id' => 'customer_id']); + } +} +``` + +If we perform the following query, we would 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: + +```php +// 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 relations for the `customer` +and the `orders` relations by calling the [[yii\db\ActiveQuery::inverseOf()|inverseOf()]] method, like the following: + +```php +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: + +```php +// 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'; +} +``` + +In the above, we have shown how to use inverse relations in lazy loading. Inverse relations also apply in +eager loading: + +```php +// SELECT * FROM customer +// SELECT * FROM order WHERE customer_id IN (1, 2, ...) +$customers = Customer::find()->with('orders')->all(); +// echoes "equal" +if ($customers[0]->orders[0]->customer === $customers[0]) { + echo 'equal'; +} else { + echo 'not equal'; +} +``` + +> Note: Inverse relation cannot be defined with a relation that involves pivoting tables. +> That is, if your relation is defined with [[yii\db\ActiveQuery::via()|via()]] or [[yii\db\ActiveQuery::viaTable()|viaTable()]], +> you cannot call [[yii\db\ActiveQuery::inverseOf()]] further. + + +Joining with Relations +---------------------- + +When working with relational databases, a common task is to join multiple tables and apply various +query conditions and parameters to the JOIN SQL statement. Instead of calling [[yii\db\ActiveQuery::join()]] +explicitly to build up the JOIN query, you may reuse the existing relation definitions and call +[[yii\db\ActiveQuery::joinWith()]] to achieve this goal. For example, + +```php +// 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(); +``` + +In the above, the method [[yii\db\ActiveQuery::innerJoinWith()|innerJoinWith()]] is a shortcut to [[yii\db\ActiveQuery::joinWith()|joinWith()]] +with the join type set as `INNER JOIN`. + +You may join with one or multiple relations; you may apply query conditions to the relations on-the-fly; +and you may also join with sub-relations. For example, + +```php +// join with multiple relations +// find the orders that contain books and were placed by customers who registered within the past 24 hours +$orders = Order::find()->innerJoinWith([ + 'books', + 'customer' => function ($query) { + $query->where('customer.created_at > ' . (time() - 24 * 3600)); + } +])->all(); +// join with sub-relations: join with books and books' authors +$orders = Order::find()->joinWith('books.author')->all(); +``` + +Behind the scenes, Yii will first execute a JOIN SQL statement to bring back the primary models +satisfying the conditions applied to the JOIN SQL. It will then execute a query for each relation +and populate the corresponding related records. + +The difference between [[yii\db\ActiveQuery::joinWith()|joinWith()]] and [[yii\db\ActiveQuery::with()|with()]] is that +the former joins the tables for the primary model class and the related model classes to retrieve +the primary models, while the latter just queries against the table for the primary model class to +retrieve the primary models. + +Because of this difference, you may apply query conditions that are only available to a JOIN SQL statement. +For example, you may filter the primary models by the conditions on the related models, like the example +above. You may also sort the primary models using columns from the related tables. + +When using [[yii\db\ActiveQuery::joinWith()|joinWith()]], you are responsible to disambiguate column names. +In the above examples, we use `item.id` and `order.id` to disambiguate the `id` column references +because both of the order table and the item table contain a column named `id`. + +By default, when you join with a relation, the relation will also be eagerly loaded. You may change this behavior +by passing the `$eagerLoading` parameter which specifies whether to eager load the specified relations. + +And also by default, [[yii\db\ActiveQuery::joinWith()|joinWith()]] uses `LEFT JOIN` to join the related tables. +You may pass it with the `$joinType` parameter to customize the join type. As a shortcut to the `INNER JOIN` type, +you may use [[yii\db\ActiveQuery::innerJoinWith()|innerJoinWith()]]. + +Below are some more examples, + +```php +// find all orders that contain books, but do not eager load "books". +$orders = Order::find()->innerJoinWith('books', false)->all(); +// which is equivalent to the above +$orders = Order::find()->joinWith('books', false, 'INNER JOIN')->all(); +``` + +Sometimes when joining two tables, you may need to specify some extra condition in the ON part of the JOIN query. +This can be done by calling the [[yii\db\ActiveQuery::onCondition()]] method like the following: + +```php +class User extends ActiveRecord +{ + public function getBooks() + { + return $this->hasMany(Item::className(), ['owner_id' => 'id'])->onCondition(['category_id' => 1]); + } +} +``` + +In the above, the [[yii\db\ActiveRecord::hasMany()|hasMany()]] method returns an [[yii\db\ActiveQuery]] instance, +upon which [[yii\db\ActiveQuery::onCondition()|onCondition()]] is called +to specify that only items whose `category_id` is 1 should be returned. + +When you perform a query using [[yii\db\ActiveQuery::joinWith()|joinWith()]], the ON condition will be put in the ON part +of the corresponding JOIN query. For example, + +```php +// SELECT user.* FROM user LEFT JOIN item ON item.owner_id=user.id AND category_id=1 +// SELECT * FROM item WHERE owner_id IN (...) AND category_id=1 +$users = User::find()->joinWith('books')->all(); +``` + +Note that if you use eager loading via [[yii\db\ActiveQuery::with()]] or lazy loading, the on-condition will be put +in the WHERE part of the corresponding SQL statement, because there is no JOIN query involved. For example, + +```php +// SELECT * FROM user WHERE id=10 +$user = User::findOne(10); +// SELECT * FROM item WHERE owner_id=10 AND category_id=1 +$books = $user->books; +``` + + +Working with Relationships +-------------------------- + +ActiveRecord provides the following two methods for establishing and breaking a +relationship between two ActiveRecord objects: + +- [[yii\db\ActiveRecord::link()|link()]] +- [[yii\db\ActiveRecord::unlink()|unlink()]] + +For example, given a customer and a new order, we can use the following code to make the +order owned by the customer: + +```php +$customer = Customer::findOne(1); +$order = new Order(); +$order->subtotal = 100; +$customer->link('orders', $order); +``` + +The [[yii\db\ActiveRecord::link()|link()]] call above will set the `customer_id` of the order to be the primary key +value of `$customer` and then call [[yii\db\ActiveRecord::save()|save()]] to save the order into the database. + + +Cross-DBMS Relations +-------------------- + +ActiveRecord allows you to establish relationships between entities from different DBMS. For example: between a relational database table and MongoDB collection. Such a relation does not require any special code: + +```php +// Relational database Active Record +class Customer extends \yii\db\ActiveRecord +{ + public static function tableName() + { + return 'customer'; + } + + public function getComments() + { + // Customer, stored in relational database, has many Comments, stored in MongoDB collection: + return $this->hasMany(Comment::className(), ['customer_id' => 'id']); + } +} + +// MongoDb Active Record +class Comment extends \yii\mongodb\ActiveRecord +{ + public static function collectionName() + { + return 'comment'; + } + + public function getCustomer() + { + // Comment, stored in MongoDB collection, has one Customer, stored in relational database: + return $this->hasOne(Customer::className(), ['id' => 'customer_id']); + } +} +``` + +All Active Record features like eager and lazy loading, establishing and breaking a relationship and so on, are +available for cross-DBMS relations. + +> Note: do not forget Active Record solutions for different DBMS may have specific methods and features, which may not be + applied for cross-DBMS relations. For example: usage of [[yii\db\ActiveQuery::joinWith()]] will obviously not work with + relation to the MongoDB collection. + + +Scopes +------ + +When you call [[yii\db\ActiveRecord::find()|find()]] or [[yii\db\ActiveRecord::findBySql()|findBySql()]], it returns an +[[yii\db\ActiveQuery|ActiveQuery]] instance. +You may call additional query methods, such as [[yii\db\ActiveQuery::where()|where()]], [[yii\db\ActiveQuery::orderBy()|orderBy()]], +to further specify the query conditions. + +It is possible that you may want to call the same set of query methods in different places. If this is the case, +you should consider defining the so-called *scopes*. A scope is essentially a method defined in a custom query class that calls a set of query methods to modify the query object. You can then use a scope instead of calling a normal query method. + +Two steps are required to define a scope. First, create a custom query class for your model and define the needed scope +methods in this class. For example, create a `CommentQuery` class for the `Comment` model and define the `active()` +scope method like the following: + +```php +namespace app\models; + +use yii\db\ActiveQuery; + +class CommentQuery extends ActiveQuery +{ + public function active($state = true) + { + $this->andWhere(['active' => $state]); + return $this; + } +} +``` + +Important points are: + +1. Class should extend from `yii\db\ActiveQuery` (or another `ActiveQuery` such as `yii\mongodb\ActiveQuery`). +2. A method should be `public` and should return `$this` in order to allow method chaining. It may accept parameters. +3. Check [[yii\db\ActiveQuery]] methods that are very useful for modifying query conditions. + +Second, override [[yii\db\ActiveRecord::find()]] to use the custom query class instead of the regular [[yii\db\ActiveQuery|ActiveQuery]]. +For the example above, you need to write the following code: + +```php +namespace app\models; + +use yii\db\ActiveRecord; + +class Comment extends ActiveRecord +{ + /** + * @inheritdoc + * @return CommentQuery + */ + public static function find() + { + return new CommentQuery(get_called_class()); + } +} +``` + +That's it. Now you can use your custom scope methods: + +```php +$comments = Comment::find()->active()->all(); +$inactiveComments = Comment::find()->active(false)->all(); +``` + +You can also use scopes when defining relations. For example, + +```php +class Post extends \yii\db\ActiveRecord +{ + public function getActiveComments() + { + return $this->hasMany(Comment::className(), ['post_id' => 'id'])->active(); + + } +} +``` + +Or use the scopes on-the-fly when performing a relational query: + +```php +$posts = Post::find()->with([ + 'comments' => function($q) { + $q->active(); + } +])->all(); +``` + +### Default Scope + +If you used Yii 1.1 before, you may know a concept called *default scope*. A default scope is a scope that +applies to ALL queries. You can define a default scope easily by overriding [[yii\db\ActiveRecord::find()]]. For example, + +```php +public static function find() +{ + return parent::find()->where(['deleted' => false]); +} +``` + +Note that all your queries should then not use [[yii\db\ActiveQuery::where()|where()]] but +[[yii\db\ActiveQuery::andWhere()|andWhere()]] and [[yii\db\ActiveQuery::orWhere()|orWhere()]] +to not override the default condition. + + +Transactional operations +--------------------- + +There are two ways of dealing with transactions while working with Active Record. First way is doing everything manually +as described in the "transactions" section of "[Database basics](db-dao.md)". Another way is to implement the +`transactions` method where you can specify which operations are to be wrapped into transactions on a per model scenario: + +```php +class Post extends \yii\db\ActiveRecord +{ + public function transactions() + { + return [ + 'admin' => self::OP_INSERT, + 'api' => self::OP_INSERT | self::OP_UPDATE | self::OP_DELETE, + // the above is equivalent to the following: + // 'api' => self::OP_ALL, + ]; + } +} +``` + +In the above `admin` and `api` are model scenarios and the constants starting with `OP_` are operations that should +be wrapped in transactions for these scenarios. Supported operations are `OP_INSERT`, `OP_UPDATE` and `OP_DELETE`. +`OP_ALL` stands for all three. + +Such automatic transactions are especially useful if you're doing additional database changes in `beforeSave`, +`afterSave`, `beforeDelete`, `afterDelete` and want to be sure that both succeeded before they are saved. + +Optimistic Locks +-------------- + +Optimistic locking allows multiple users to access the same record for edits and avoids +potential conflicts. For example, when a user attempts to save the record upon some staled data +(because another user has modified the data), a [[\yii\db\StaleObjectException]] exception will be thrown, +and the update or deletion is skipped. + +Optimistic locking is only supported by `update()` and `delete()` methods and isn't used by default. + +To use Optimistic locking: + +1. Create a column to store the version number of each row. The column type should be `BIGINT DEFAULT 0`. + Override the `optimisticLock()` method to return the name of this column. +2. In the Web form that collects the user input, add a hidden field that stores + the lock version of the recording being updated. +3. In the controller action that does the data updating, try to catch the [[\yii\db\StaleObjectException]] + and implement necessary business logic (e.g. merging the changes, prompting stated data) + to resolve the conflict. + +Dirty Attributes +-------------- + +An attribute is considered dirty if its value was modified after the model was loaded from database or since the most recent data save. When saving record data by calling `save()`, `update()`, `insert()` etc. only dirty attributes are saved into the database. If there are no dirty attributes then there is nothing to be saved so no query will be issued at all. + +See also +-------- + +- [Model](structure-models.md) +- [[yii\db\ActiveRecord]] From 1496d507cb2e5c2ae84a0b88944cd0aedc521186 Mon Sep 17 00:00:00 2001 From: Nobuo Kihara Date: Fri, 5 Dec 2014 09:26:52 +0900 Subject: [PATCH 3/7] docs/guide-ja/README.md - added translation status infos [ci skip] --- docs/guide-ja/README.md | 106 ++++++++++++++++++++++++------------------------ 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/docs/guide-ja/README.md b/docs/guide-ja/README.md index 4d6c53e..f4d2f94 100644 --- a/docs/guide-ja/README.md +++ b/docs/guide-ja/README.md @@ -60,15 +60,15 @@ All Rights Reserved. 鍵となる概念 ------------ -* [コンポーネント](concept-components.md) -* [プロパティ](concept-properties.md) -* [イベント](concept-events.md) -* [ビヘイビア](concept-behaviors.md) -* [構成情報](concept-configurations.md) -* [エイリアス](concept-aliases.md) -* [クラスのオートロード](concept-autoloading.md) -* [サービスロケータ](concept-service-locator.md) -* [依存注入コンテナ](concept-di-container.md) +* **翻訳中** [コンポーネント](concept-components.md) +* **翻訳中** [プロパティ](concept-properties.md) +* **翻訳中** [イベント](concept-events.md) +* **翻訳中** [ビヘイビア](concept-behaviors.md) +* **翻訳中** [構成情報](concept-configurations.md) +* **翻訳中** [エイリアス](concept-aliases.md) +* **翻訳中** [クラスのオートロード](concept-autoloading.md) +* **翻訳中** [サービスロケータ](concept-service-locator.md) +* **翻訳中** [依存注入コンテナ](concept-di-container.md) データベースの取り扱い @@ -76,8 +76,8 @@ All Rights Reserved. * [データアクセスオブジェクト](db-dao.md): データベースへの接続、基本的なクエリ、トランザクション、および、スキーマ操作 * [クエリビルダ](db-query-builder.md): シンプルな抽象レイヤを使ってデータベースに対してクエリを行う -* [アクティブレコード](db-active-record.md): アクティブレコード ORM、レコードの読み出しと操作、リレーションの定義 -* [マイグレーション](db-migrations.md): チーム開発環境においてデータベースにバージョンコントロールを適用 +* **翻訳中** [アクティブレコード](db-active-record.md): アクティブレコード ORM、レコードの読み出しと操作、リレーションの定義 +* **翻訳中** [マイグレーション](db-migrations.md): チーム開発環境においてデータベースにバージョンコントロールを適用 * **TBD** [Sphinx](db-sphinx.md) * **TBD** [Redis](db-redis.md) * **TBD** [MongoDB](db-mongodb.md) @@ -87,30 +87,30 @@ All Rights Reserved. ユーザからのデータ取得 ---------------------- -* [フォームを作成する](input-forms.md) -* [入力を検証する](input-validation.md) -* [ファイルをアップロードする](input-file-upload.md) +* **翻訳未着手** [フォームを作成する](input-forms.md) +* **翻訳未着手** [入力を検証する](input-validation.md) +* **翻訳未着手** [ファイルをアップロードする](input-file-upload.md) * **TBD** [複数モデルのためのデータ取得](input-multiple-models.md) データの表示 ------------ -* [データの書式設定](output-formatter.md) +* **翻訳未着手** [データの書式設定](output-formatter.md) * **TBD** [ページネーション](output-pagination.md) * **TBD** [並べ替え](output-sorting.md) -* [データプロバイダ](output-data-providers.md) -* [データウィジェット](output-data-widgets.md) -* [クライアントスクリプトを扱う](output-client-scripts.md) -* [テーマ](output-theming.md) +* **翻訳未着手** [データプロバイダ](output-data-providers.md) +* **翻訳未着手** [データウィジェット](output-data-widgets.md) +* **翻訳未着手** [クライアントスクリプトを扱う](output-client-scripts.md) +* **翻訳未着手** [テーマ](output-theming.md) セキュリティ ------------ -* [認証](security-authentication.md) -* [権限](security-authorization.md) -* [パスワードを扱う](security-passwords.md) +* **翻訳未着手** [認証](security-authentication.md) +* **翻訳未着手** [権限](security-authorization.md) +* **翻訳未着手** [パスワードを扱う](security-passwords.md) * **TBD** [Auth クライアント](security-auth-clients.md) * **TBD** [最善の慣行](security-best-practices.md) @@ -128,49 +128,49 @@ All Rights Reserved. RESTful ウェブサービス ---------------------- -* [クイックスタート](rest-quick-start.md) -* [リソース](rest-resources.md) -* [コントローラ](rest-controllers.md) -* [ルーティング](rest-routing.md) -* [レスポンスの書式設定](rest-response-formatting.md) -* [認証](rest-authentication.md) -* [転送レート制限](rest-rate-limiting.md) -* [バージョン管理](rest-versioning.md) -* [エラー処理](rest-error-handling.md) +* **翻訳未着手** [クイックスタート](rest-quick-start.md) +* **翻訳未着手** [リソース](rest-resources.md) +* **翻訳未着手** [コントローラ](rest-controllers.md) +* **翻訳未着手** [ルーティング](rest-routing.md) +* **翻訳未着手** [レスポンスの書式設定](rest-response-formatting.md) +* **翻訳未着手** [認証](rest-authentication.md) +* **翻訳未着手** [転送レート制限](rest-rate-limiting.md) +* **翻訳未着手** [バージョン管理](rest-versioning.md) +* **翻訳未着手** [エラー処理](rest-error-handling.md) 開発ツール ---------- -* [デバッグツールバーとデバッガ](tool-debugger.md) -* [Gii を使ってコードを生成する](tool-gii.md) +* **翻訳未着手** [デバッグツールバーとデバッガ](tool-debugger.md) +* **翻訳未着手** [Gii を使ってコードを生成する](tool-gii.md) * **TBD** [API ドキュメントを生成する](tool-api-doc.md) テスト ------ -* [概要](test-overview.md) -* [テスト環境の構築](test-environment-setup.md) -* [ユニットテスト](test-unit.md) -* [機能テスト](test-functional.md) -* [承認テスト](test-acceptance.md) -* [フィクスチャ](test-fixtures.md) +* **翻訳未着手** [概要](test-overview.md) +* **翻訳未着手** [テスト環境の構築](test-environment-setup.md) +* **翻訳未着手** [ユニットテスト](test-unit.md) +* **翻訳未着手** [機能テスト](test-functional.md) +* **翻訳未着手** [承認テスト](test-acceptance.md) +* **翻訳未着手** [フィクスチャ](test-fixtures.md) スペシャルトピック ------------------ -* [アドバンストアプリケーションテンプレート](tutorial-advanced-app.md) -* [アプリケーションを一から構築する](tutorial-start-from-scratch.md) -* [コンソールコマンド](tutorial-console.md) -* [コアのバリデータ](tutorial-core-validators.md) -* [国際化](tutorial-i18n.md) -* [メール](tutorial-mailing.md) -* [パフォーマンスチューニング](tutorial-performance-tuning.md) -* [共有ホスト環境](tutorial-shared-hosting.md) -* [テンプレートエンジン](tutorial-template-engines.md) -* [サードパーティのコードを扱う](tutorial-yii-integration.md) +* **翻訳未着手** [アドバンストアプリケーションテンプレート](tutorial-advanced-app.md) +* **翻訳未着手** [アプリケーションを一から構築する](tutorial-start-from-scratch.md) +* **翻訳未着手** [コンソールコマンド](tutorial-console.md) +* **翻訳未着手** [コアのバリデータ](tutorial-core-validators.md) +* **翻訳未着手** [国際化](tutorial-i18n.md) +* **翻訳未着手** [メール](tutorial-mailing.md) +* **翻訳未着手** [パフォーマンスチューニング](tutorial-performance-tuning.md) +* **翻訳未着手** [共有ホスト環境](tutorial-shared-hosting.md) +* **翻訳未着手** [テンプレートエンジン](tutorial-template-engines.md) +* **翻訳未着手** [サードパーティのコードを扱う](tutorial-yii-integration.md) ウィジェット @@ -191,9 +191,9 @@ RESTful ウェブサービス ヘルパ ------ -* [概要](helper-overview.md) -* [ArrayHelper](helper-array.md) -* [Html](helper-html.md) -* [Url](helper-url.md) +* **翻訳未着手** [概要](helper-overview.md) +* **翻訳未着手** [ArrayHelper](helper-array.md) +* **翻訳未着手** [Html](helper-html.md) +* **翻訳未着手** [Url](helper-url.md) * **TBD** [Security](helper-security.md) From 429247cd33f31e498a87ff756172a809341c5bb9 Mon Sep 17 00:00:00 2001 From: Nobuo Kihara Date: Sat, 6 Dec 2014 00:43:02 +0900 Subject: [PATCH 4/7] docs/guide-ja/db-active-record.md - WIP [ci skip] --- docs/guide-ja/db-active-record.md | 114 ++++++++++++++++++-------------------- 1 file changed, 54 insertions(+), 60 deletions(-) diff --git a/docs/guide-ja/db-active-record.md b/docs/guide-ja/db-active-record.md index 18b0a8c..e4b12f2 100644 --- a/docs/guide-ja/db-active-record.md +++ b/docs/guide-ja/db-active-record.md @@ -1,17 +1,14 @@ -Active Record -============= +アクティブレコード +================== -> Note: This section is under development. +> Note|注意: この節はまだ執筆中です。 -[Active Record](http://en.wikipedia.org/wiki/Active_record_pattern) provides an object-oriented interface -for accessing data stored in a database. An Active Record class is associated with a database table, -an Active Record instance corresponds to a row of that table, and an attribute of an Active Record -instance represents the value of a column in that row. Instead of writing raw SQL statements, -you can work with Active Record in an object-oriented fashion to manipulate the data in database tables. +[アクティブレコード](http://ja.wikipedia.org/wiki/Active_Record) は、データベースに保存されているデータにアクセスするために、オブジェクト指向のインタフェイスを提供するものです。 +アクティブレコードクラスはデータベーステーブルと関連付けられて、アクティブレコードのインスタンスがそのテーブルの行に対応し、アクティブレコードのインスタンスの属性がその行のカラムの値を表現します。 +生の SQL 文を書く代りに、アクティブレコードを使って、オブジェクト指向の流儀でデータベーステーブルのデータを操作することが出来ます。 -For example, assume `Customer` is an Active Record class is associated with the `customer` table -and `name` is a column of the `customer` table. You can write the following code to insert a new -row into the `customer` table: +例えば、`Customer` が `customer` テーブルに関連付けられたアクティブレコードクラスであり、`name` が `customer` テーブルのカラムであると仮定しましょう。 +`customer` テーブルに新しい行を挿入するために次のコードを書くことが出来ます。 ```php $customer = new Customer(); @@ -19,8 +16,7 @@ $customer->name = 'Qiang'; $customer->save(); ``` -The above code is equivalent to using the following raw SQL statement, which is less -intuitive, more error prone, and may have compatibility problems for different DBMS: +上記のコードは、次のように生の SQL 文を使うのと等価なものですが、生の SQL 文の方は、直感的でなく、間違いも生じやすく、また、DBMS の違いによる互換性の問題も生じ得ます。 ```php $db->createCommand('INSERT INTO customer (name) VALUES (:name)', [ @@ -28,30 +24,29 @@ $db->createCommand('INSERT INTO customer (name) VALUES (:name)', [ ])->execute(); ``` -Below is the list of databases that are currently supported by Yii Active Record: +下記が、現在 Yii のアクティブレコードによってサポートされているデータベースのリストです。 -* MySQL 4.1 or later: via [[yii\db\ActiveRecord]] -* PostgreSQL 7.3 or later: via [[yii\db\ActiveRecord]] -* SQLite 2 and 3: via [[yii\db\ActiveRecord]] -* Microsoft SQL Server 2010 or later: via [[yii\db\ActiveRecord]] -* Oracle: via [[yii\db\ActiveRecord]] -* CUBRID 9.3 or later: via [[yii\db\ActiveRecord]] (Note that due to a [bug](http://jira.cubrid.org/browse/APIS-658) in - the cubrid PDO extension, quoting of values will not work, so you need CUBRID 9.3 as the client as well as the server) -* Sphnix: via [[yii\sphinx\ActiveRecord]], requires the `yii2-sphinx` extension -* ElasticSearch: via [[yii\elasticsearch\ActiveRecord]], requires the `yii2-elasticsearch` extension -* Redis 2.6.12 or later: via [[yii\redis\ActiveRecord]], requires the `yii2-redis` extension -* MongoDB 1.3.0 or later: via [[yii\mongodb\ActiveRecord]], requires the `yii2-mongodb` extension +* MySQL 4.1 以降: [[yii\db\ActiveRecord]] による。 +* PostgreSQL 7.3 以降: [[yii\db\ActiveRecord]] による。 +* SQLite 2 および 3: [[yii\db\ActiveRecord]] による。 +* Microsoft SQL Server 2010 以降: [[yii\db\ActiveRecord]] による。 +* Oracle: [[yii\db\ActiveRecord]] による。 +* CUBRID 9.3 以降: [[yii\db\ActiveRecord]] による。(cubrid PDO 拡張の [バグ](http://jira.cubrid.org/browse/APIS-658) + のために、値を引用符で囲む機能が動作しません。そのため、サーバだけでなくクライアントも CUBRID 9.3 が必要になります) +* Sphnix: [[yii\sphinx\ActiveRecord]] による。`yii2-sphinx` エクステンションが必要。 +* ElasticSearch: [[yii\elasticsearch\ActiveRecord]] による。`yii2-elasticsearch` エクステンションが必要。 +* Redis 2.6.12 以降: [[yii\redis\ActiveRecord]] による。`yii2-redis` エクステンションが必要。 +* MongoDB 1.3.0 以降: [[yii\mongodb\ActiveRecord]] による。`yii2-mongodb` エクステンションが必要。 -As you can see, Yii provides Active Record support for relational databases as well as NoSQL databases. -In this tutorial, we will mainly describe the usage of Active Record for relational databases. -However, most content described here are also applicable to Active Record for NoSQL databases. +ご覧のように、Yii はリレーショナルデータベースだけでなく NoSQL データベースに対してもアクティブレコードのサポートを提供しています。 +このチュートリアルでは、主としてリレーショナルデータベースのためのアクティブレコードの使用方法を説明します。 +しかし、ここで説明するほとんどの内容は NoSQL データベースのためのアクティブレコードにも適用することが出来るものです。 -Declaring Active Record Classes ------------------------------- +アクティブレコードクラスを宣言する +---------------------------------- -To declare an Active Record class you need to extend [[yii\db\ActiveRecord]] and implement -the `tableName` method that returns the name of the database table associated with the class: +アクティブレコードクラスを宣言するためには、[[yii\db\ActiveRecord]] を拡張して、クラスと関連付けられるデータベーステーブルの名前を返す `tableName` メソッドを実装する必要があります。 ```php namespace app\models; @@ -64,7 +59,7 @@ class Customer extends ActiveRecord const STATUS_DELETED = 'deleted'; /** - * @return string the name of the table associated with this ActiveRecord class. + * @return string アクティブレコードクラスと関連付けられるデータベーステーブルの名前 */ public static function tableName() { @@ -74,22 +69,22 @@ class Customer extends ActiveRecord ``` -Accessing Column Data ---------------------- +カラムのデータにアクセスする +---------------------------- -Active Record maps each column of the corresponding database table row to an attribute in the Active Record -object. An attribute behaves like a regular object public property. The name of an attribute is the same -as the corresponding column name and is case-sensitive. +アクティブレコードは、対応するデータベーステーブルの行の各カラムをアクティブレコードオブジェクトの属性に割り付けます。 +属性は通常のオブジェクトのパブリックなプロパティと同様の振る舞いをします。 +属性の名前は対応するから無名と同じであり、大文字と小文字を区別します。 -To read the value of a column, you can use the following syntax: +カラムの値を読み出すために、次の構文を使用することが出来ます。 ```php -// "id" and "email" are the names of columns in the table associated with the $customer ActiveRecord object +// "id" と "email" は、$customer アクティブレコードオブジェクトと関連付けられたテーブルのカラム名 $id = $customer->id; $email = $customer->email; ``` -To change the value of a column, assign a new value to the associated property and save the object: +カラムの値を変更するためには、関連付けられたプロパティに新しい値を代入して、オブジェクトを保存します。 ```php $customer->email = 'jane@example.com'; @@ -97,12 +92,12 @@ $customer->save(); ``` -Connecting to Database +データベースに接続する ---------------------- -Active Record uses a [[yii\db\Connection|DB connection]] to exchange data with the database. By default, -it uses the `db` [application component](structure-application-components.md) as the connection. As explained in [Database basics](db-dao.md), -you may configure the `db` component in the application configuration file as follows, +アクティブレコードは、データベースとの間でデータを交換するために [[yii\db\Connection|DB 接続]] を使用します。 +既定では、アクティブレコードは `db` [アプリケーションコンポーネント](structure-application-components.md) を接続として使用します。 +[データベースの基礎](db-dao.md) で説明したように、次のようにして、アプリケーションの構成情報ファイルの中で `db` コンポーネントを構成することが出来ます。 ```php return [ @@ -117,8 +112,7 @@ return [ ]; ``` -If you are using multiple databases in your application and you want to use a different DB connection -for your Active Record class, you may override the [[yii\db\ActiveRecord::getDb()|getDb()]] method: +アプリケーションの中で複数のデータベースを使っており、アクティブレコードクラスのために異なる DB 接続を使いたい場合は、[[yii\db\ActiveRecord::getDb()|getDb()]] メソッドをオーバーライドすることが出来ます。 ```php class Customer extends ActiveRecord @@ -127,46 +121,46 @@ class Customer extends ActiveRecord public static function getDb() { - return \Yii::$app->db2; // use the "db2" application component + return \Yii::$app->db2; // "db2" アプリケーションコンポーネントを使用 } } ``` -Querying Data from Database ---------------------------- +データベースにデータを問い合わせる +---------------------------------- -Active Record provides two entry methods for building DB queries and populating data into Active Record instances: +アクティブレコードは、DB クエリを構築してアクティブレコードインスタンスにデータを投入するために、二つの入力メソッドを提供しています。 - [[yii\db\ActiveRecord::find()]] - [[yii\db\ActiveRecord::findBySql()]] -Both methods return an [[yii\db\ActiveQuery]] instance, which extends [[yii\db\Query]], and thus supports the same set -of flexible and powerful DB query building methods, such as `where()`, `join()`, `orderBy()`, etc. The following examples -demonstrate some of the possibilities. +この二つのメソッドは [[yii\db\ActiveQuery]] のインスタンスを返します。 + [[yii\db\ActiveQuery]] は [[yii\db\Query]] を拡張したものであり、従って、[[yii\db\Query]] と同じ一連の柔軟かつ強力な DB クエリ構築メソッド、例えば、`where()`、`join()`、`orderBy()` 等を提供します。 +下記の例は、いくつかの可能性を示すものです。 ```php -// to retrieve all *active* customers and order them by their ID: +// *アクティブ* な顧客を全て読み出して、その ID によって並べ替える $customers = Customer::find() ->where(['status' => Customer::STATUS_ACTIVE]) ->orderBy('id') ->all(); -// to return a single customer whose ID is 1: +// ID が 1 である一人の顧客を返す $customer = Customer::find() ->where(['id' => 1]) ->one(); -// to return the number of *active* customers: +// *アクティブ* な顧客の数を返す $count = Customer::find() ->where(['status' => Customer::STATUS_ACTIVE]) ->count(); -// to index the result by customer IDs: +// 結果を顧客 ID によってインデックスする $customers = Customer::find()->indexBy('id')->all(); -// $customers array is indexed by customer IDs +// $customers 配列は顧客 ID によってインデックスされる -// to retrieve customers using a raw SQL statement: +// 生の SQL 文を使って顧客を読み出す $sql = 'SELECT * FROM customer'; $customers = Customer::findBySql($sql)->all(); ``` From 2b2c43df5f23bf6cf57f6a0457f4e8f36bdb7b2d Mon Sep 17 00:00:00 2001 From: Nobuo Kihara Date: Sat, 6 Dec 2014 14:03:22 +0900 Subject: [PATCH 5/7] docs/guide-ja/db-active-record.md - WIP [ci skip] --- docs/guide-ja/db-active-record.md | 136 +++++++++++++++++++------------------- 1 file changed, 67 insertions(+), 69 deletions(-) diff --git a/docs/guide-ja/db-active-record.md b/docs/guide-ja/db-active-record.md index e4b12f2..0312b38 100644 --- a/docs/guide-ja/db-active-record.md +++ b/docs/guide-ja/db-active-record.md @@ -165,93 +165,92 @@ $sql = 'SELECT * FROM customer'; $customers = Customer::findBySql($sql)->all(); ``` -> Tip: In the code above `Customer::STATUS_ACTIVE` is a constant defined in `Customer`. It is a good practice to - use meaningful constant names rather than hardcoded strings or numbers in your code. +> Tip|ヒント: 上記のコードでは、`Customer::STATUS_ACTIVE` は `Customer` で定義されている定数です。 + コードの中で、ハードコードされた文字列や数字ではなく、意味が分かる名前の定数を使用することは良い慣行です。 -Two shortcut methods are provided to return Active Record instances matching a primary key value or a set of -column values: `findOne()` and `findAll()`. The former returns the first matching instance while the latter -returns all of them. For example, +プライマリキーの値または一連のカラムの値に合致するアクティブレコードのインスタンスを返すためのショートカットメソッドが二つ提供されています。 +すなわち、`findOne()` と `findAll()` です。 +前者は合致する最初のインスタンスを返し、後者は合致する全てのインスタンスを返します。 +例えば、 ```php -// to return a single customer whose ID is 1: +// ID が 1 である顧客を一人返す $customer = Customer::findOne(1); -// to return an *active* customer whose ID is 1: +// ID が 1 である *アクティブ* な顧客を一人返す $customer = Customer::findOne([ 'id' => 1, 'status' => Customer::STATUS_ACTIVE, ]); -// to return customers whose ID is 1, 2 or 3: +// ID が 1、2、または 3 である顧客を全て返す $customers = Customer::findAll([1, 2, 3]); -// to return customers whose status is "deleted": +// 状態が「削除済み」である顧客を全て返す $customer = Customer::findAll([ 'status' => Customer::STATUS_DELETED, ]); ``` -> Note: By default neither `findOne()` nor `one()` will add `LIMIT 1` to the query. This is fine and preferred - if you know the query will return only one or a few rows of data (e.g. if you are querying with some primary keys). - However, if the query may potentially return many rows of data, you should call `limit(1)` to improve the performance. - For example, `Customer::find()->where(['status' => Customer::STATUS_ACTIVE])->limit(1)->one()`. +> Note: デフォルトでは、`findOne()` も `one()` も、クエリに `LIMIT 1` を追加しません。 + クエリが一つだけまたは少数の行のデータしか返さないことが分かっている場合 (例えば、プライマリキーか何かでクエリをする場合) は、これで十分であり、また、この方が望ましいでしょう。 + しかし、クエリが多数の行のデータを返す可能性がある場合は、パフォーマンスを向上させるために `limit(1)` を呼ぶべきです。 + 例えば、`Customer::find()->where(['status' => Customer::STATUS_ACTIVE])->limit(1)->one()` のように。 -### Retrieving Data in Arrays +### データを配列に読み出す -Sometimes when you are processing a large amount of data, you may want to use arrays to hold the data -retrieved from database to save memory. This can be done by calling `asArray()`: +大量のデータを処理する場合には、メモリ使用量を節約するために、データベースから取得したデータを配列に保持したいこともあるでしょう。 +これは、`asArray()` を呼ぶことによって実現できます。 ```php -// to return customers in terms of arrays rather than `Customer` objects: +// 顧客を `Customer` オブジェクトでなく配列の形式で返す $customers = Customer::find() ->asArray() ->all(); -// each element of $customers is an array of name-value pairs +// $customers の各要素は、「名前-値」のペアの配列 ``` -Note that while this method saves memory and improves performance it is a step to a lower abstraction -layer and you will loose some features that the active record layer has. -Fetching data using asArray is nearly equal to running a normal query using the [query builder](db-dao.md). -When using asArray the result will be returned as a simple array with no typecasting performed -so the result may contain string values for fields that are integer when accessed on the active record object. +このメソッドはメモリを節約してパフォーマンスを向上させますが、低い抽象レイヤに向って一歩を踏み出すものであり、アクティブレコードのレイヤが持ついくつかの機能を失うことになるという点に注意してください。 +`asArray` を使ってデータを読み出すことは、[クエリビルダ](db-dao.md) を使って普通のクエリを実行するのと、ほとんど同じことです。 +`asArray` を使うと、結果は、型変換の実行を伴わない単純な配列になります。 +その結果、アクティブレコードオブジェクトでアクセスする場合には整数になるフィールドが、文字列の値を含むことがあり得ます。 -### Retrieving Data in Batches +### データをバッチモードで読み出す -In [Query Builder](db-query-builder.md), we have explained that you may use *batch query* to minimize your memory -usage when querying a large amount of data from the database. You may use the same technique -in Active Record. For example, +[クエリビルダ](db-query-builder.md) において、大量のデータをデータベースから検索する場合に、メモリ使用量を最小化するために *バッチクエリ* を使うことが出来るということを説明しました。 +おなじテクニックをアクティブレコードでも使うことが出来ます。 +例えば、 ```php -// fetch 10 customers at a time +// 一度に 10 人の顧客を読み出す foreach (Customer::find()->batch(10) as $customers) { - // $customers is an array of 10 or fewer Customer objects + // $customers は 10 以下の Customer オブジェクトの配列 } -// fetch 10 customers at a time and iterate them one by one +// 一度に 10 人の顧客を読み出して、一人ずつ反復する foreach (Customer::find()->each(10) as $customer) { - // $customer is a Customer object + // $customer は Customer オブジェクト } -// batch query with eager loading +// いーがーローディングをするバッチクエリ foreach (Customer::find()->with('orders')->each() as $customer) { } ``` -Manipulating Data in Database ------------------------------ +データベースのデータを操作する +------------------------------ -Active Record provides the following methods to insert, update and delete a single row in a table associated with -a single Active Record instance: +アクティブレコードは、一つのアクティブレコードインスタンスに関連付けられたテーブルの一行を挿入、更新または削除するために、次のメソッドを提供しています。 - [[yii\db\ActiveRecord::save()|save()]] - [[yii\db\ActiveRecord::insert()|insert()]] - [[yii\db\ActiveRecord::update()|update()]] - [[yii\db\ActiveRecord::delete()|delete()]] -Active Record also provides the following static methods that apply to a whole table associated with -an Active Record class. Be extremely careful when using these methods as they affect the whole table. -For example, `deleteAll()` will delete ALL rows in the table. +アクティブレコードは、アクティブレコードクラスと関連付けられたテーブル全体に適用する、次の静的なメソッドを提供しています。 +これらのメソッドはテーブル全体に影響を与えますので、使用するときはこの上なく注意深くしなければなりません。 +例えば、`deleteAll()` はテーブルの全ての行を削除します。 - [[yii\db\ActiveRecord::updateCounters()|updateCounters()]] - [[yii\db\ActiveRecord::updateAll()|updateAll()]] @@ -259,78 +258,77 @@ For example, `deleteAll()` will delete ALL rows in the table. - [[yii\db\ActiveRecord::deleteAll()|deleteAll()]] -The following examples show how to use these methods: +次の例は、これらのメソッドの使用方法を示すものです。 ```php -// to insert a new customer record +// 新しい customer のレコードを挿入する $customer = new Customer(); $customer->name = 'James'; $customer->email = 'james@example.com'; -$customer->save(); // equivalent to $customer->insert(); +$customer->save(); // $customer->insert() と等値 -// to update an existing customer record +// 既存の customer のレコードを更新する $customer = Customer::findOne($id); $customer->email = 'james@example.com'; -$customer->save(); // equivalent to $customer->update(); +$customer->save(); // $customer->update() と等値 -// to delete an existing customer record +// 既存の customer のレコードを削除する $customer = Customer::findOne($id); $customer->delete(); -// to delete several customers +// いくつかの customer のレコードを削除する Customer::deleteAll('age > :age AND gender = :gender', [':age' => 20, ':gender' => 'M']); -// to increment the age of ALL customers by 1 +// すべてのレコードの年齢に 1 を追加する Customer::updateAllCounters(['age' => 1]); ``` -> Info: The `save()` method will call either `insert()` or `update()`, depending on whether - the Active Record instance is new or not (internally it will check the value of [[yii\db\ActiveRecord::isNewRecord]]). - If an Active Record is instantiated via the `new` operator, calling `save()` will - insert a row in the table; calling `save()` on an active record fetched from the database will update the corresponding - row in the table. +> Info|情報: `save()` メソッドは、アクティブレコードインスタンスが新しいものであるか否かに従って、`insert()` または `update()` を呼びます + (内部的には、[[yii\db\ActiveRecord::isNewRecord]] の値をチェックして判断します)。 + アクティブレコードのインスタンスが `new` 演算子によって作成された場合は、`save()` を呼ぶと、テーブルに新しい行が挿入されます。 + データベースから読み出されたアクティブレコードに対して `save()` を呼ぶと、テーブルの中の対応する行が更新されます。 -### Data Input and Validation +### データの入力と検証 -Because Active Record extends from [[yii\base\Model]], it supports the same data input and validation features -as described in [Model](structure-models.md). For example, you may declare validation rules by overwriting the -[[yii\base\Model::rules()|rules()]] method; you may massively assign user input data to an Active Record instance; -and you may call [[yii\base\Model::validate()|validate()]] to trigger data validation. +アクティブレコードは [[yii\base\Model]] を拡張したものですので、[モデル](structure-models.md) で説明したのと同じデータ入力と検証の機能をサポートしています。 +例えば、[[yii\base\Model::rules()|rules()]] メソッドをオーバーライドして検証規則を宣言することが出来ます。 +アクティブレコードインスタンスにユーザの入力データを一括代入することも出来ます。 +また、[[yii\base\Model::validate()|validate()]] を呼んで、データ検証を実行させることも出来ます。 -When you call `save()`, `insert()` or `update()`, these methods will automatically call [[yii\base\Model::validate()|validate()]]. -If the validation fails, the corresponding data saving operation will be cancelled. +`save()`、`insert()` または `update()` を呼ぶと、これらのメソッドが自動的に [[yii\base\Model::validate()|validate()]] を呼びます。 +検証が失敗すると、対応するデータ保存操作はキャンセルされます。 -The following example shows how to use an Active Record to collect/validate user input and save them into the database: +次の例は、アクティブレコードを使ってユーザ入力を収集/検証してデータベースに保存する方法を示すものです。 ```php -// creating a new record +// 新しいレコードを作成する $model = new Customer; if ($model->load(Yii::$app->request->post()) && $model->save()) { - // the user input has been collected, validated and saved + // ユーザ入力が収集、検証されて、保存された } -// updating a record whose primary key is $id +// プライマリキーが $id であるレコードを更新する $model = Customer::findOne($id); if ($model === null) { throw new NotFoundHttpException; } if ($model->load(Yii::$app->request->post()) && $model->save()) { - // the user input has been collected, validated and saved + // ユーザ入力が収集、検証されて、保存された } ``` -### Loading Default Values +### デフォルト値を読み出す -Your table columns may be defined with default values. Sometimes, you may want to pre-populate your -Web form for an Active Record with these values. To do so, call the -[[yii\db\ActiveRecord::loadDefaultValues()|loadDefaultValues()]] method before rendering the form: +テーブルのカラムの定義は、デフォルト値を含むことが出来ます。 +アクティブレコードのためのウェブフォームに、このデフォルト値を事前に代入しておきたい場合があるでしょう。 +そうするためには、フォームを表示する前に、[[yii\db\ActiveRecord::loadDefaultValues()|loadDefaultValues()]] を呼びます。 ```php $customer = new Customer(); $customer->loadDefaultValues(); -// ... render HTML form for $customer ... +// ... $customer の HTML フォームを表示する ... ``` If you want to set some initial values for the attributes yourself you can override the `init()` method From c9b3f5054f4b6a0c96c152155dcf2393ef2c60af Mon Sep 17 00:00:00 2001 From: Nobuo Kihara Date: Sun, 7 Dec 2014 21:46:50 +0900 Subject: [PATCH 6/7] docs/guide-ja/db-active-record.md - WIP [ci skip] --- docs/guide-ja/db-active-record.md | 148 ++++++++++++++++++-------------------- 1 file changed, 68 insertions(+), 80 deletions(-) diff --git a/docs/guide-ja/db-active-record.md b/docs/guide-ja/db-active-record.md index 0312b38..763115b 100644 --- a/docs/guide-ja/db-active-record.md +++ b/docs/guide-ja/db-active-record.md @@ -331,72 +331,68 @@ $customer->loadDefaultValues(); // ... $customer の HTML フォームを表示する ... ``` -If you want to set some initial values for the attributes yourself you can override the `init()` method -of the active record class and set the values there. For example to set the default value for the `status` attribute: +属性に対して何かの初期値を自分自身で設定したい場合は、アクティブレコードクラスの `init()` メソッドをオーバーライドして、そこで値を設定することが出来ます。 +例えば、`status` 属性のデフォルト値を設定したい場合は、 ```php public function init() { parent::init(); - $this->status = 'active'; + $this->status = self::STATUS_ACTIVE; } ``` -Active Record Life Cycles -------------------------- +アクティブレコードのライフサイクル +---------------------------------- -It is important to understand the life cycles of Active Record when it is used to manipulate data in database. -These life cycles are typically associated with corresponding events which allow you to inject code -to intercept or respond to these events. They are especially useful for developing Active Record [behaviors](concept-behaviors.md). +アクティブレコードがデータベースのデータの操作に使われるときのライフサイクルを理解しておくことは重要なことです。 +そのライフサイクルは、概して、対応するイベントと関連付けられており、それらのイベントに対して干渉したり反応したりするコードを注入できるようになっています。 +これらのイベントは特にアクティブレコードの [ビヘイビア](concept-behaviors.md) を開発するときに役に立ちます。 -When instantiating a new Active Record instance, we will have the following life cycles: +アクティブレコードの新しいインスタンスを作成する場合は、次のライフサイクルを経ます。 -1. constructor -2. [[yii\db\ActiveRecord::init()|init()]]: will trigger an [[yii\db\ActiveRecord::EVENT_INIT|EVENT_INIT]] event +1. コンストラクタ +2. [[yii\db\ActiveRecord::init()|init()]]: [[yii\db\ActiveRecord::EVENT_INIT|EVENT_INIT]] イベントをトリガ -When querying data through the [[yii\db\ActiveRecord::find()|find()]] method, we will have the following life cycles -for EVERY newly populated Active Record instance: +[[yii\db\ActiveRecord::find()|find()]] メソッドによってデータを検索する場合は、新しくデータを投入されるアクティブレコードの全てが、それぞれ、次のライフサイクルを経ます。 -1. constructor -2. [[yii\db\ActiveRecord::init()|init()]]: will trigger an [[yii\db\ActiveRecord::EVENT_INIT|EVENT_INIT]] event -3. [[yii\db\ActiveRecord::afterFind()|afterFind()]]: will trigger an [[yii\db\ActiveRecord::EVENT_AFTER_FIND|EVENT_AFTER_FIND]] event +1. コンストラクタ +2. [[yii\db\ActiveRecord::init()|init()]]: [[yii\db\ActiveRecord::EVENT_INIT|EVENT_INIT]] イベントをトリガ +3. [[yii\db\ActiveRecord::afterFind()|afterFind()]]: [[yii\db\ActiveRecord::EVENT_AFTER_FIND|EVENT_AFTER_FIND]] イベントをトリガ -When calling [[yii\db\ActiveRecord::save()|save()]] to insert or update an ActiveRecord, we will have -the following life cycles: +[[yii\db\ActiveRecord::save()|save()]] を呼んで、アクティブレコードを挿入または更新する場合は、次のライフサイクルを経ます。 -1. [[yii\db\ActiveRecord::beforeValidate()|beforeValidate()]]: will trigger an [[yii\db\ActiveRecord::EVENT_BEFORE_VALIDATE|EVENT_BEFORE_VALIDATE]] event -2. [[yii\db\ActiveRecord::afterValidate()|afterValidate()]]: will trigger an [[yii\db\ActiveRecord::EVENT_AFTER_VALIDATE|EVENT_AFTER_VALIDATE]] event -3. [[yii\db\ActiveRecord::beforeSave()|beforeSave()]]: will trigger an [[yii\db\ActiveRecord::EVENT_BEFORE_INSERT|EVENT_BEFORE_INSERT]] or [[yii\db\ActiveRecord::EVENT_BEFORE_UPDATE|EVENT_BEFORE_UPDATE]] event -4. perform the actual data insertion or updating -5. [[yii\db\ActiveRecord::afterSave()|afterSave()]]: will trigger an [[yii\db\ActiveRecord::EVENT_AFTER_INSERT|EVENT_AFTER_INSERT]] or [[yii\db\ActiveRecord::EVENT_AFTER_UPDATE|EVENT_AFTER_UPDATE]] event +1. [[yii\db\ActiveRecord::beforeValidate()|beforeValidate()]]: [[yii\db\ActiveRecord::EVENT_BEFORE_VALIDATE|EVENT_BEFORE_VALIDATE]] イベントをトリガ +2. [[yii\db\ActiveRecord::afterValidate()|afterValidate()]]: [[yii\db\ActiveRecord::EVENT_AFTER_VALIDATE|EVENT_AFTER_VALIDATE]] イベントをトリガ +3. [[yii\db\ActiveRecord::beforeSave()|beforeSave()]]: [[yii\db\ActiveRecord::EVENT_BEFORE_INSERT|EVENT_BEFORE_INSERT]] または [[yii\db\ActiveRecord::EVENT_BEFORE_UPDATE|EVENT_BEFORE_UPDATE]] イベントをトリガ +4. 実際のデータ挿入または更新を実行 +5. [[yii\db\ActiveRecord::afterSave()|afterSave()]]: [[yii\db\ActiveRecord::EVENT_AFTER_INSERT|EVENT_AFTER_INSERT]] または [[yii\db\ActiveRecord::EVENT_AFTER_UPDATE|EVENT_AFTER_UPDATE]] イベントをトリガ -And finally, when calling [[yii\db\ActiveRecord::delete()|delete()]] to delete an ActiveRecord, we will have -the following life cycles: +最後に、[[yii\db\ActiveRecord::delete()|delete()]] を呼んで、アクティブレコードを削除する場合は、次のライフサイクルを経ます。 -1. [[yii\db\ActiveRecord::beforeDelete()|beforeDelete()]]: will trigger an [[yii\db\ActiveRecord::EVENT_BEFORE_DELETE|EVENT_BEFORE_DELETE]] event -2. perform the actual data deletion -3. [[yii\db\ActiveRecord::afterDelete()|afterDelete()]]: will trigger an [[yii\db\ActiveRecord::EVENT_AFTER_DELETE|EVENT_AFTER_DELETE]] event +1. [[yii\db\ActiveRecord::beforeDelete()|beforeDelete()]]: [[yii\db\ActiveRecord::EVENT_BEFORE_DELETE|EVENT_BEFORE_DELETE]] イベントをトリガ +2. 実際のデータ削除を実行 +3. [[yii\db\ActiveRecord::afterDelete()|afterDelete()]]: [[yii\db\ActiveRecord::EVENT_AFTER_DELETE|EVENT_AFTER_DELETE]] イベントをトリガ -Working with Relational Data ----------------------------- +リレーショナルデータを扱う +-------------------------- -You can use ActiveRecord to also query a table's relational data (i.e., selection of data from Table A can also pull -in related data from Table B). Thanks to ActiveRecord, the relational data returned can be accessed like a property -of the ActiveRecord object associated with the primary table. +テーブルのリレーショナルデータもアクティブレコードを使ってクエリすることが出来ます +(すなわち、テーブル A のデータを選択すると、テーブル B の関連付けられたデータも一緒に取り込むことが出来ます)。 +アクティブレコードのおかげで、返されるリレーショナルデータは、プライマリテーブルと関連付けられたアクティブレコードオブジェクトのプロパティのようにアクセスすることが出来ます。 -For example, with an appropriate relation declaration, by accessing `$customer->orders` you may obtain -an array of `Order` objects which represent the orders placed by the specified customer. +例えば、適切なリレーションが宣言されていれば、`$customer->orders` にアクセスすることによって、指定された顧客が発行した注文を表す `Order` オブジェクトの配列を取得することが出来ます。 -To declare a relation, define a getter method which returns an [[yii\db\ActiveQuery]] object that has relation -information about the relation context and thus will only query for related records. For example, +リレーションを宣言するためには、[[yii\db\ActiveQuery]] オブジェクトを返すゲッターメソッドを定義します。そして、その [[yii\db\ActiveQuery]] オブジェクトは、リレーションのコンテキストに関する情報を持ち、従って関連するレコードだけをクエリするものとします。 +例えば、 ```php class Customer extends \yii\db\ActiveRecord { public function getOrders() { - // Customer has_many Order via Order.customer_id -> id + // Customer は Order.customer_id -> id によって、複数の Order を持つ return $this->hasMany(Order::className(), ['customer_id' => 'id']); } } @@ -405,47 +401,44 @@ class Order extends \yii\db\ActiveRecord { public function getCustomer() { - // Order has_one Customer via Customer.id -> customer_id + // Order は Customer.id -> customer_id によって、一つの Customer を持つ return $this->hasOne(Customer::className(), ['id' => 'customer_id']); } } ``` -The methods [[yii\db\ActiveRecord::hasMany()]] and [[yii\db\ActiveRecord::hasOne()]] used in the above -are used to model the many-one relationship and one-one relationship in a relational database. -For example, a customer has many orders, and an order has one customer. -Both methods take two parameters and return an [[yii\db\ActiveQuery]] object: +上記の例で使用されている [[yii\db\ActiveRecord::hasMany()]] と [[yii\db\ActiveRecord::hasOne()]] のメソッドは、リレーショナルデータベースにおける多対一と一対一の関係を表現するために使われます。 +例えば、顧客 (customer) は複数の注文 (order) を持ち、注文 (order) は一つの顧客 (customer)を持つ、という関係です。 +これらのメソッドはともに二つのパラメータを取り、[[yii\db\ActiveQuery]] オブジェクトを返します。 - - `$class`: the name of the class of the related model(s). This should be a fully qualified class name. - - `$link`: the association between columns from the two tables. This should be given as an array. - The keys of the array are the names of the columns from the table associated with `$class`, - while the values of the array are the names of the columns from the declaring class. - It is a good practice to define relationships based on table foreign keys. + - `$class`: 関連するモデルのクラス名。これは完全修飾のクラス名でなければなりません。 + - `$link`: 二つのテーブルに属するカラム間の関係。これは配列として与えられなければなりません。 + 配列のキーは、`$class` と関連付けられるテーブルにあるカラムの名前であり、配列の値はリレーションを宣言しているクラスのテーブルにあるカラムの名前です。 + リレーションをテーブルの外部キーに基づいて定義するのが望ましい慣行です。 -After declaring relations, getting relational data is as easy as accessing a component property -that is defined by the corresponding getter method: +リレーションを宣言した後は、リレーショナルデータを取得することは、対応するゲッターメソッドで定義されているコンポーネントのプロパティを取得するのと同じように、とても簡単なことになります。 ```php -// get the orders of a customer +// 顧客の注文を取得する $customer = Customer::findOne(1); -$orders = $customer->orders; // $orders is an array of Order objects +$orders = $customer->orders; // $orders は Order オブジェクトの配列 ``` -Behind the scenes, the above code executes the following two SQL queries, one for each line of code: +舞台裏では、上記のコードは、各行について一つずつ、次の二つの SQL クエリを実行します。 ```sql SELECT * FROM customer WHERE id=1; SELECT * FROM order WHERE customer_id=1; ``` -> Tip: If you access the expression `$customer->orders` again, it will not perform the second SQL query again. -The SQL query is only performed the first time when this expression is accessed. Any further -accesses will only return the previously fetched results that are cached internally. If you want to re-query -the relational data, simply unset the existing expression first: `unset($customer->orders);`. +> Tip|情報: `$customer->orders` という式に再びアクセスした場合は、第二の SQL クエリはもう実行されません。 + 第二の SQL クエリは、この式が最初にアクセスされた時だけ実行されます。 + 二度目以降のアクセスでは、内部的にキャッシュされている以前に読み出した結果が返されるだけです。 + リレーショナルデータを再クエリしたい場合は、単純に、まず既存の式を未設定状態に戻して (`unset($customer->orders);`) から、再度、`$customer->orders` にアクセスします。 -Sometimes, you may want to pass parameters to a relational query. For example, instead of returning -all orders of a customer, you may want to return only big orders whose subtotal exceeds a specified amount. -To do so, declare a `bigOrders` relation with the following getter method: +場合によっては、リレーショナルクエリにパラメータを渡したいことがあります。 +例えば、顧客の注文を全て返す代りに、小計が指定した金額を超える大きな注文だけを返したいことがあるでしょう。 +そうするためには、次のようなゲッターメソッドで `bigOrders` リレーションを宣言します。 ```php class Customer extends \yii\db\ActiveRecord @@ -459,32 +452,28 @@ class Customer extends \yii\db\ActiveRecord } ``` -Remember that `hasMany()` returns an [[yii\db\ActiveQuery]] object which allows you to customize the query by -calling the methods of [[yii\db\ActiveQuery]]. +`hasMany()` が 返す [[yii\db\ActiveQuery]] は、[[yii\db\ActiveQuery]] のメソッドを呼ぶことでクエリをカスタマイズ出来るものであることを覚えておいてください。 -With the above declaration, if you access `$customer->bigOrders`, it will only return the orders -whose subtotal is greater than 100. To specify a different threshold value, use the following code: +上記の宣言によって、`$customer->bigOrders` にアクセスした場合は、小計が 100 以上である注文だけが返されることになります。 +異なる閾値を指定するためには、次のコードを使用します。 ```php $orders = $customer->getBigOrders(200)->all(); ``` -> Note: A relation method returns an instance of [[yii\db\ActiveQuery]]. If you access the relation like -an attribute (i.e. a class property), the return value will be the query result of the relation, which could be an instance of [[yii\db\ActiveRecord]], -an array of that, or null, depending on the multiplicity of the relation. For example, `$customer->getOrders()` returns -an `ActiveQuery` instance, while `$customer->orders` returns an array of `Order` objects (or an empty array if -the query results in nothing). +> Note|注意: リレーションメソッドは [[yii\db\ActiveQuery]] のインスタンスを返します。 +リレーションを属性 (すなわち、クラスのプロパティ) としてアクセスした場合は、返り値はリレーションのクエリ結果となります。 +クエリ結果は、リレーションが複数のレコードを返すものか否かに応じて、[[yii\db\ActiveRecord]] の一つのインスタンス、またはその配列、または null となります。 +例えば、`$customer->getOrders()` は `ActiveQuery` のインスタンスを返し、`$customer->orders` は `Order` オブジェクトの配列 (またはクエリ結果が無い場合は空の配列) を返します。 -Relations with Junction Table ------------------------------ +連結テーブルを使うリレーション +------------------------------ -Sometimes, two tables are related together via an intermediary table called a [junction table][]. To declare such relations, -we can customize the [[yii\db\ActiveQuery]] object by calling its [[yii\db\ActiveQuery::via()|via()]] or -[[yii\db\ActiveQuery::viaTable()|viaTable()]] method. +場合によっては、二つのテーブルが [連結テーブル][] と呼ばれる中間的なテーブルによって関連付けられていることがあります。 +そのようなリレーションを宣言するために、[[yii\db\ActiveQuery::via()|via()]] または [[yii\db\ActiveQuery::viaTable()|viaTable()]] メソッドを呼んで、[[yii\db\ActiveQuery]] オブジェクトをカスタマイズすることが出来ます。 -For example, if table `order` and table `item` are related via the junction table `order_item`, -we can declare the `items` relation in the `Order` class like the following: +例えば、テーブル `order` とテーブル `item` が連結テーブル `order_item` によって関連付けられている場合、`Order` クラスにおいて `items` リレーションを次のように宣言することが出来ます。 ```php class Order extends \yii\db\ActiveRecord @@ -497,9 +486,8 @@ class Order extends \yii\db\ActiveRecord } ``` -The [[yii\db\ActiveQuery::via()|via()]] method is similar to [[yii\db\ActiveQuery::viaTable()|viaTable()]] except that -the first parameter of [[yii\db\ActiveQuery::via()|via()]] takes a relation name declared in the ActiveRecord class -instead of the junction table name. For example, the above `items` relation can be equivalently declared as follows: +[[yii\db\ActiveQuery::via()|via()]] メソッドは、最初のパラメータとして、結合テーブルの名前ではなく、アクティブレコードクラスで宣言されているリレーションの名前を取ること以外は、[[yii\db\ActiveQuery::viaTable()|viaTable()]] と同じです。 +例えば、上記の `items` リレーションは次のように宣言しても等値です。 ```php class Order extends \yii\db\ActiveRecord @@ -517,7 +505,7 @@ class Order extends \yii\db\ActiveRecord } ``` -[junction table]: https://en.wikipedia.org/wiki/Junction_table "Junction table on Wikipedia" +[連結テーブル]: https://en.wikipedia.org/wiki/Junction_table "Junction table on Wikipedia" Lazy and Eager Loading From b86d86ac23c5447a3d75d9f47358b5a338bc2cd0 Mon Sep 17 00:00:00 2001 From: Nobuo Kihara Date: Tue, 9 Dec 2014 00:06:41 +0900 Subject: [PATCH 7/7] docs/guide-ja/db-active-record.md - completed [ci skip] --- docs/guide-ja/db-active-record.md | 339 ++++++++++++++++++-------------------- 1 file changed, 161 insertions(+), 178 deletions(-) diff --git a/docs/guide-ja/db-active-record.md b/docs/guide-ja/db-active-record.md index 763115b..cbcd1cf 100644 --- a/docs/guide-ja/db-active-record.md +++ b/docs/guide-ja/db-active-record.md @@ -508,79 +508,79 @@ class Order extends \yii\db\ActiveRecord [連結テーブル]: https://en.wikipedia.org/wiki/Junction_table "Junction table on Wikipedia" -Lazy and Eager Loading ----------------------- +レイジーローディングとイーガーローディング +------------------------------------------ -As described earlier, when you access the related objects for the first time, ActiveRecord will perform a DB query -to retrieve the corresponding data and populate it into the related objects. No query will be performed -if you access the same related objects again. We call this *lazy loading*. For example, +前に述べたように、関連オブジェクトに最初にアクセスしたときに、アクティブレコードは DB クエリを実行して関連データを読み出し、それを関連オブジェクトに投入します。 +同じ関連オブジェクトに再度アクセスしても、クエリは実行されません。 +これを *レイジーローディング* と呼びます。 +例えば、 ```php -// SQL executed: SELECT * FROM customer WHERE id=1 +// 実行される SQL: SELECT * FROM customer WHERE id=1 $customer = Customer::findOne(1); -// SQL executed: SELECT * FROM order WHERE customer_id=1 +// 実行される SQL: SELECT * FROM order WHERE customer_id=1 $orders = $customer->orders; -// no SQL executed +// SQL は実行されない $orders2 = $customer->orders; ``` -Lazy loading is very convenient to use. However, it may suffer from a performance issue in the following scenario: +レイジーローディングは非常に使い勝手が良いものです。しかし、次のシナリオでは、パフォーマンスの問題を生じ得ます。 ```php -// SQL executed: SELECT * FROM customer LIMIT 100 +// 実行される SQL: SELECT * FROM customer WHERE id=1 $customers = Customer::find()->limit(100)->all(); foreach ($customers as $customer) { - // SQL executed: SELECT * FROM order WHERE customer_id=... + // 実行される SQL: SELECT * FROM order WHERE customer_id=... $orders = $customer->orders; - // ...handle $orders... + // ... $orders を処理 ... } ``` -How many SQL queries will be performed in the above code, assuming there are more than 100 customers in -the database? 101! The first SQL query brings back 100 customers. Then for each customer, a SQL query -is performed to bring back the orders of that customer. +データベースに 100 人以上の顧客が登録されていると仮定した場合、上記のコードで何個の SQL クエリが実行されるでしようか? +101 です。最初の SQL クエリが 100 人の顧客を返します。 +次に、100 人の顧客全てについて、それぞれ、顧客の注文を返すための SQL クエリが実行されます。 -To solve the above performance problem, you can use the so-called *eager loading* approach by calling [[yii\db\ActiveQuery::with()]]: +上記のパフォーマンスの問題を解決するためには、[[yii\db\ActiveQuery::with()]] を呼んでいわゆる *イーガーローディング* を使うことが出来ます。 ```php -// SQL executed: SELECT * FROM customer LIMIT 100; -// SELECT * FROM orders WHERE customer_id IN (1,2,...) +// 実行される SQL: SELECT * FROM customer LIMIT 100; +// SELECT * FROM orders WHERE customer_id IN (1,2,...) $customers = Customer::find()->limit(100) ->with('orders')->all(); foreach ($customers as $customer) { - // no SQL executed + // SQL は実行されない $orders = $customer->orders; - // ...handle $orders... + // ... $orders を処理 ... } ``` -As you can see, only two SQL queries are needed for the same task! +ご覧のように、同じ仕事をするのに必要な SQL クエリがたった二つになります。 -> Info: In general, if you are eager loading `N` relations among which `M` relations are defined with `via()` or `viaTable()`, -> a total number of `1+M+N` SQL queries will be performed: one query to bring back the rows for the primary table, one for -> each of the `M` junction tables corresponding to the `via()` or `viaTable()` calls, and one for each of the `N` related tables. +> Info|情報: 一般化して言うと、`N` 個のリレーションのうち `M` 個のリレーションが `via()` または `viaTable()` によって定義されている場合、この `N` 個のリレーションをイーガーロードしようとすると、合計で `1+M+N` 個の SQL クエリが実行されます。 +> 主たるテーブルの行を返すために一つ、`via()` または `viaTable()` の呼び出しに対応する `M` 個の連結テーブルのそれぞれに対して一つずつ、そして、`N` 個の関連テーブルのそれぞれに対して一つずつ、という訳です。 -> Note: When you are customizing `select()` with eager loading, make sure you include the columns that link -> the related models. Otherwise, the related models will not be loaded. For example, +> Note|注意: イーガーローディングで `select()` をカスタマイズしようとする場合は、関連モデルにリンクするカラムを必ず含めてください。 +> そうしないと、関連モデルは読み出されません。例えば、 ```php $orders = Order::find()->select(['id', 'amount'])->with('customer')->all(); -// $orders[0]->customer is always null. To fix the problem, you should do the following: +// $orders[0]->customer は常に null になる。この問題を解決するためには、次のようにしなければならない。 $orders = Order::find()->select(['id', 'amount', 'customer_id'])->with('customer')->all(); ``` -Sometimes, you may want to customize the relational queries on the fly. This can be -done for both lazy loading and eager loading. For example, +場合によっては、リレーショナルクエリをその場でカスタマイズしたいことがあるでしょう。 +これは、レイジーローディングでもイーガーローディングでも、可能です。例えば、 ```php $customer = Customer::findOne(1); -// lazy loading: SELECT * FROM order WHERE customer_id=1 AND subtotal>100 +// レイジーローディング: SELECT * FROM order WHERE customer_id=1 AND subtotal>100 $orders = $customer->getOrders()->where('subtotal>100')->all(); -// eager loading: SELECT * FROM customer LIMIT 100 -// SELECT * FROM order WHERE customer_id IN (1,2,...) AND subtotal>100 +// イーガーローディング: SELECT * FROM customer LIMIT 100 +// SELECT * FROM order WHERE customer_id IN (1,2,...) AND subtotal>100 $customers = Customer::find()->limit(100)->with([ 'orders' => function($query) { $query->andWhere('subtotal>100'); @@ -589,11 +589,11 @@ $customers = Customer::find()->limit(100)->with([ ``` -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`: +リレーションは、たいていの場合、ペアで定義することが出来ます。 +例えば、`Customer` が `orders` という名前のリレーションを持ち、`Order` が `customer` という名前のリレーションを持つ、ということがあります。 ```php class Customer extends ActiveRecord @@ -615,25 +615,23 @@ class Order extends ActiveRecord } ``` -If we perform the following query, we would 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: +次に例示するクエリを実行すると、注文 (order) のリレーションとして取得した顧客 (customer) が、最初にその注文をリレーションとして取得した顧客とは別の Customer オブジェクトになってしまうことに気付くでしょう。 +また、`customer->orders` にアクセスすると一個の SQL が実行され、`order->customer` にアクセスするともう一つ別の SQL が実行されるということにも気付くでしょう。 ```php // 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'; + echo '等しい'; } else { - echo 'not equal'; + echo '等しくない'; } ``` -To avoid the redundant execution of the last SQL statement, we could declare the inverse relations for the `customer` -and the `orders` relations by calling the [[yii\db\ActiveQuery::inverseOf()|inverseOf()]] method, like the following: +冗長な最後の SQL 文の実行を避けるためには、次のように、[[yii\db\ActiveQuery::inverseOf()|inverseOf()]] メソッドを呼んで、`customer` と `oerders` のリレーションに対して逆リレーションを宣言することが出来ます。 ```php class Customer extends ActiveRecord @@ -646,109 +644,101 @@ class Customer extends ActiveRecord } ``` -Now if we execute the same query as shown above, we would get: +こうすると、上記と同じクエリを実行したときに、次の結果を得ることが出来ます。 ```php // 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'; + echo '等しい'; } else { - echo 'not equal'; + echo '等しくない'; } ``` -In the above, we have shown how to use inverse relations in lazy loading. Inverse relations also apply in -eager loading: +上記では、レイジーローディングにおいて逆リレーションを使う方法を示しました。 +逆リレーションはイーガーローディングにも適用されます。 ```php // SELECT * FROM customer // SELECT * FROM order WHERE customer_id IN (1, 2, ...) $customers = Customer::find()->with('orders')->all(); -// echoes "equal" +// "等しい" がエコーされる if ($customers[0]->orders[0]->customer === $customers[0]) { - echo 'equal'; + echo '等しい'; } else { - echo 'not equal'; + echo '等しくない'; } ``` -> Note: Inverse relation cannot be defined with a relation that involves pivoting tables. -> That is, if your relation is defined with [[yii\db\ActiveQuery::via()|via()]] or [[yii\db\ActiveQuery::viaTable()|viaTable()]], -> you cannot call [[yii\db\ActiveQuery::inverseOf()]] further. +> Note|注意: 逆リレーションはピボットテーブルを含むリレーションに対しては定義することが出来ません。 +> つまり、リレーションが [[yii\db\ActiveQuery::via()|via()]] または [[yii\db\ActiveQuery::viaTable()|viaTable()]] によって定義されている場合は、[[yii\db\ActiveQuery::inverseOf()]] を追加で呼ぶことは出来ません。 -Joining with Relations ----------------------- +リレーションを使ってテーブルを結合する +-------------------------------------- -When working with relational databases, a common task is to join multiple tables and apply various -query conditions and parameters to the JOIN SQL statement. Instead of calling [[yii\db\ActiveQuery::join()]] -explicitly to build up the JOIN query, you may reuse the existing relation definitions and call -[[yii\db\ActiveQuery::joinWith()]] to achieve this goal. For example, +リレーショナルデータベースを扱う場合、複数のテーブルを結合して、JOIN SQL 文にさまざまなクエリ条件とパラメータを指定することは、ごく当り前の仕事です。 +その目的を達するために、[[yii\db\ActiveQuery::join()]] を明示的に呼んで JOIN クエリを構築する代りに、既存のリレーション定義を再利用して [[yii\db\ActiveQuery::joinWith()]] を呼ぶことが出来ます。 +例えば、 ```php -// find all orders and sort the orders by the customer id and the order id. also eager loading "customer" +// 全ての注文を検索して、注文を顧客 ID と注文 ID でソートする。同時に "customer" をイーガーロードする。 $orders = Order::find()->joinWith('customer')->orderBy('customer.id, order.id')->all(); -// find all orders that contain books, and eager loading "books" +// 書籍を含む全ての注文を検索し、"books" をイーガーロードする。 $orders = Order::find()->innerJoinWith('books')->all(); ``` -In the above, the method [[yii\db\ActiveQuery::innerJoinWith()|innerJoinWith()]] is a shortcut to [[yii\db\ActiveQuery::joinWith()|joinWith()]] -with the join type set as `INNER JOIN`. +上記において、[[yii\db\ActiveQuery::innerJoinWith()|innerJoinWith()]] メソッドは、結合タイプを `INNER JOIN` とする [[yii\db\ActiveQuery::joinWith()|joinWith()]] へのショートカットです。 -You may join with one or multiple relations; you may apply query conditions to the relations on-the-fly; -and you may also join with sub-relations. For example, +一個または複数のリレーションを結合することが出来ます。リレーションにクエリ条件をその場で適用することも出来ます。 +また、サブリレーションを結合することも出来ます。例えば、 ```php -// join with multiple relations -// find the orders that contain books and were placed by customers who registered within the past 24 hours +// 複数のリレーションを結合 +// 書籍を含む注文で、過去 24 時間以内に登録した顧客によって発行された注文を検索する $orders = Order::find()->innerJoinWith([ 'books', 'customer' => function ($query) { $query->where('customer.created_at > ' . (time() - 24 * 3600)); } ])->all(); -// join with sub-relations: join with books and books' authors +// サブリレーションとの結合: 書籍および書籍の著者を結合 $orders = Order::find()->joinWith('books.author')->all(); ``` -Behind the scenes, Yii will first execute a JOIN SQL statement to bring back the primary models -satisfying the conditions applied to the JOIN SQL. It will then execute a query for each relation -and populate the corresponding related records. +舞台裏では、Yii は最初に JOIN SQL 文を実行して、その JOIN SQL に適用された条件を満たす主たるモデルを取得します。 +そして、次にリレーションごとのクエリを実行して、対応する関連レコードを投入します。 -The difference between [[yii\db\ActiveQuery::joinWith()|joinWith()]] and [[yii\db\ActiveQuery::with()|with()]] is that -the former joins the tables for the primary model class and the related model classes to retrieve -the primary models, while the latter just queries against the table for the primary model class to -retrieve the primary models. +[[yii\db\ActiveQuery::joinWith()|joinWith()]] と [[yii\db\ActiveQuery::with()|with()]] の違いは、前者が主たるモデルクラスのテーブルと関連モデルクラスのテーブルを結合して主たるモデルを読み出すのに対して、後者は主たるモデルクラスのテーブルに対してだけクエリを実行して主たるモデルを読み出す、という点にあります。 -Because of this difference, you may apply query conditions that are only available to a JOIN SQL statement. -For example, you may filter the primary models by the conditions on the related models, like the example -above. You may also sort the primary models using columns from the related tables. +この違いによって、[[yii\db\ActiveQuery::joinWith()|joinWith()]] では、JOIN SQL 文だけに指定できるクエリ条件を適用することが出来ます。 +例えば、上記の例のように、関連モデルに対する条件によって主たるモデルをフィルタすることが出来ます。 +主たるモデルを関連テーブルのカラムを使って並び替えることも出来ます。 -When using [[yii\db\ActiveQuery::joinWith()|joinWith()]], you are responsible to disambiguate column names. -In the above examples, we use `item.id` and `order.id` to disambiguate the `id` column references -because both of the order table and the item table contain a column named `id`. +[[yii\db\ActiveQuery::joinWith()|joinWith()]] を使うときは、カラム名の曖昧さを解決することについて、あなたが責任を負わなければなりません。 +上記の例では、order テーブルと item テーブルがともに `id` という名前のカラムを持っているため、`item.id` と `order.id` を使って、`id` カラムの参照の曖昧さを解決しています。 -By default, when you join with a relation, the relation will also be eagerly loaded. You may change this behavior -by passing the `$eagerLoading` parameter which specifies whether to eager load the specified relations. +既定では、リレーションを結合すると、リレーションがイーガーロードされることにもなります。 +この既定の動作は、指定されたリレーションをイーガーロードするかどうかを規定する `$eagerLoading` パラメータを渡して、変更することが出来ます。 -And also by default, [[yii\db\ActiveQuery::joinWith()|joinWith()]] uses `LEFT JOIN` to join the related tables. -You may pass it with the `$joinType` parameter to customize the join type. As a shortcut to the `INNER JOIN` type, -you may use [[yii\db\ActiveQuery::innerJoinWith()|innerJoinWith()]]. +また、既定では、[[yii\db\ActiveQuery::joinWith()|joinWith()]] は関連テーブルを結合するのに `LEFT JOIN` を使います。 +結合タイプをカスタマイズするために `$joinType` パラメータを渡すことが出来ます。 +`INNER JOIN` タイプのためのショートカットとして、[[yii\db\ActiveQuery::innerJoinWith()|innerJoinWith()]] を使うことが出来ます。 -Below are some more examples, +下記に、いくつかの例を追加します。 ```php -// find all orders that contain books, but do not eager load "books". +// 書籍を含む注文を全て検索するが、"books" はイーガーロードしない。 $orders = Order::find()->innerJoinWith('books', false)->all(); -// which is equivalent to the above +// これも上と等値 $orders = Order::find()->joinWith('books', false, 'INNER JOIN')->all(); ``` -Sometimes when joining two tables, you may need to specify some extra condition in the ON part of the JOIN query. -This can be done by calling the [[yii\db\ActiveQuery::onCondition()]] method like the following: +二つのテーブルを結合するとき、場合によっては、JOIN クエリの ON の部分で何らかの追加条件を指定する必要があります。 +これは、次のように、[[yii\db\ActiveQuery::onCondition()]] メソッドを呼んで実現することが出来ます。 ```php class User extends ActiveRecord @@ -760,12 +750,11 @@ class User extends ActiveRecord } ``` -In the above, the [[yii\db\ActiveRecord::hasMany()|hasMany()]] method returns an [[yii\db\ActiveQuery]] instance, -upon which [[yii\db\ActiveQuery::onCondition()|onCondition()]] is called -to specify that only items whose `category_id` is 1 should be returned. +上記においては、[[yii\db\ActiveRecord::hasMany()|hasMany()]] メソッドが [[yii\db\ActiveQuery]] のインスタンスを返しています。 +そして、それに対して [[yii\db\ActiveQuery::onCondition()|onCondition()]] が呼ばれて、`category_id` が 1 である品目だけが返されるべきことを指定しています。 -When you perform a query using [[yii\db\ActiveQuery::joinWith()|joinWith()]], the ON condition will be put in the ON part -of the corresponding JOIN query. For example, +[[yii\db\ActiveQuery::joinWith()|joinWith()]] を使ってクエリを実行すると、指定された ON 条件が対応する JOIN クエリの ON の部分に挿入されます。 +例えば、 ```php // SELECT user.* FROM user LEFT JOIN item ON item.owner_id=user.id AND category_id=1 @@ -773,8 +762,8 @@ of the corresponding JOIN query. For example, $users = User::find()->joinWith('books')->all(); ``` -Note that if you use eager loading via [[yii\db\ActiveQuery::with()]] or lazy loading, the on-condition will be put -in the WHERE part of the corresponding SQL statement, because there is no JOIN query involved. For example, +[[yii\db\ActiveQuery::with()]] を使ってイーガーロードする場合や、レイジーロードする場合には、JOIN クエリは使われないため、ON 条件が対応する SQL 文の WHERE の部分に挿入されることに注意してください。 +例えば、 ```php // SELECT * FROM user WHERE id=10 @@ -784,17 +773,15 @@ $books = $user->books; ``` -Working with Relationships --------------------------- +関連付けを扱う +-------------- -ActiveRecord provides the following two methods for establishing and breaking a -relationship between two ActiveRecord objects: +アクティブレコードは、二つのアクティブレコードオブジェクト間の関連付けを確立および破棄するために、次の二つのメソッドを提供しています。 - [[yii\db\ActiveRecord::link()|link()]] - [[yii\db\ActiveRecord::unlink()|unlink()]] -For example, given a customer and a new order, we can use the following code to make the -order owned by the customer: +例えば、顧客と新しい注文があると仮定したとき、次のコードを使って、その注文をその顧客のものとすることが出来ます。 ```php $customer = Customer::findOne(1); @@ -803,17 +790,17 @@ $order->subtotal = 100; $customer->link('orders', $order); ``` -The [[yii\db\ActiveRecord::link()|link()]] call above will set the `customer_id` of the order to be the primary key -value of `$customer` and then call [[yii\db\ActiveRecord::save()|save()]] to save the order into the database. +上記の [[yii\db\ActiveRecord::link()|link()]] の呼び出しは、注文の `customer_id` に `$customer` のプライマリキーの値を設定し、[[yii\db\ActiveRecord::save()|save()]] を呼んで注文をデータベースに保存します。 -Cross-DBMS Relations --------------------- +DBMS 間のリレーション +--------------------- -ActiveRecord allows you to establish relationships between entities from different DBMS. For example: between a relational database table and MongoDB collection. Such a relation does not require any special code: +アクティブレコードは、異なる DBMS に属するエンティティ間、例えば、リレーショナルデータベースのテーブルと MongoDB のコレクションの間に、リレーションを確立することを可能にしています。 +そのようなリレーションでも、何も特別なコードは必要ありません。 ```php -// Relational database Active Record +// リレーショナルデータベースのアクティブレコード class Customer extends \yii\db\ActiveRecord { public static function tableName() @@ -823,12 +810,12 @@ class Customer extends \yii\db\ActiveRecord public function getComments() { - // Customer, stored in relational database, has many Comments, stored in MongoDB collection: + // リレーショナルデータベースに保存されている Customer は、MongoDB コレクションに保存されている複数の Comment を持つ return $this->hasMany(Comment::className(), ['customer_id' => 'id']); } } -// MongoDb Active Record +// MongoDb のアクティブレコード class Comment extends \yii\mongodb\ActiveRecord { public static function collectionName() @@ -838,34 +825,32 @@ class Comment extends \yii\mongodb\ActiveRecord public function getCustomer() { - // Comment, stored in MongoDB collection, has one Customer, stored in relational database: + // MongoDB コレクションに保存されている Comment は、リレーショナルデータベースに保存されている一つの Customer を持つ return $this->hasOne(Customer::className(), ['id' => 'customer_id']); } } ``` -All Active Record features like eager and lazy loading, establishing and breaking a relationship and so on, are -available for cross-DBMS relations. +アクティブレコードの全ての機能、例えば、イーガーローディングやレイジーローディング、関連付けの確立や破棄などが、DBMS 間のリレーションでも利用可能です。 -> Note: do not forget Active Record solutions for different DBMS may have specific methods and features, which may not be - applied for cross-DBMS relations. For example: usage of [[yii\db\ActiveQuery::joinWith()]] will obviously not work with - relation to the MongoDB collection. +> Note|注意: DBMS ごとのアクティブレコードの実装には、DBMS 固有のメソッドや機能が含まれる場合があり、そういうものは DBMS 間のリレーションには適用できないということを忘れないでください。 + 例えば、[[yii\db\ActiveQuery::joinWith()]] の使用が MongoDB コレクションに対するリレーションでは動作しないことは明白です。 -Scopes ------- +スコープ +-------- -When you call [[yii\db\ActiveRecord::find()|find()]] or [[yii\db\ActiveRecord::findBySql()|findBySql()]], it returns an -[[yii\db\ActiveQuery|ActiveQuery]] instance. -You may call additional query methods, such as [[yii\db\ActiveQuery::where()|where()]], [[yii\db\ActiveQuery::orderBy()|orderBy()]], -to further specify the query conditions. +[[yii\db\ActiveRecord::find()|find()]] または [[yii\db\ActiveRecord::findBySql()|findBySql()]] を呼ぶと、[[yii\db\ActiveQuery|ActiveQuery]] のインスタンスが返されます。 +そして、追加のクエリメソッド、例えば、[[yii\db\ActiveQuery::where()|where()]] や [[yii\db\ActiveQuery::orderBy()|orderBy()]] を呼んで、クエリ条件をさらに指定することが出来ます。 -It is possible that you may want to call the same set of query methods in different places. If this is the case, -you should consider defining the so-called *scopes*. A scope is essentially a method defined in a custom query class that calls a set of query methods to modify the query object. You can then use a scope instead of calling a normal query method. +別々の場所で同じ一連のクエリメソッドを呼びたいということがあり得ます。 +そのような場合には、いわゆる *スコープ* を定義することを検討すべきです。 +スコープは、本質的には、カスタムクエリクラスの中で定義されたメソッドであり、クエリオブジェクトを修正する一連のメソッドを呼ぶものです。 +スコープを定義しておくと、通常のクエリメソッドを呼ぶ代りに、スコープを使うことが出来るようになります。 -Two steps are required to define a scope. First, create a custom query class for your model and define the needed scope -methods in this class. For example, create a `CommentQuery` class for the `Comment` model and define the `active()` -scope method like the following: +スコープを定義するためには二つのステップが必要です。 +最初に、モデルのためのカスタムクエリクラスを作成して、このクラスの中に必要なスコープメソッドを定義します。 +例えば、`Comment` モデルのために `CommentQuery` クラスを作成して、次のように、`active()` というスコープメソッドを定義します。 ```php namespace app\models; @@ -882,14 +867,14 @@ class CommentQuery extends ActiveQuery } ``` -Important points are: +重要な点は、以下の通りです。 -1. Class should extend from `yii\db\ActiveQuery` (or another `ActiveQuery` such as `yii\mongodb\ActiveQuery`). -2. A method should be `public` and should return `$this` in order to allow method chaining. It may accept parameters. -3. Check [[yii\db\ActiveQuery]] methods that are very useful for modifying query conditions. +1. クラスは `yii\db\ActiveQuery` (または、`yii\mongodb\ActiveQuery` などの、その他の `ActiveQuery`) を拡張したものにしなければなりません。 +2. メソッドは `public` で、メソッドチェーンが出来るように `$this` を返さなければなりません。メソッドはパラメータを取ることが出来ます。 +3. クエリ条件を修正する方法については、[[yii\db\ActiveQuery]] のメソッド群を参照するのが非常に役に立ちます。 -Second, override [[yii\db\ActiveRecord::find()]] to use the custom query class instead of the regular [[yii\db\ActiveQuery|ActiveQuery]]. -For the example above, you need to write the following code: +次に、[[yii\db\ActiveRecord::find()]] をオーバーライドして、通常の [[yii\db\ActiveQuery|ActiveQuery]] の代りに、カスタムクエリクラスを使うようにします。 +上記の例のためには、次のコードを書く必要があります。 ```php namespace app\models; @@ -909,14 +894,14 @@ class Comment extends ActiveRecord } ``` -That's it. Now you can use your custom scope methods: +以上です。これで、カスタムスコープメソッドを使用することが出来ます。 ```php $comments = Comment::find()->active()->all(); $inactiveComments = Comment::find()->active(false)->all(); ``` -You can also use scopes when defining relations. For example, +リレーションを定義するときにもスコープを使用することが出来ます。例えば、 ```php class Post extends \yii\db\ActiveRecord @@ -929,7 +914,7 @@ class Post extends \yii\db\ActiveRecord } ``` -Or use the scopes on-the-fly when performing a relational query: +または、リレーショナルクエリを実行するときに、その場でスコープを使うことも出来ます。 ```php $posts = Post::find()->with([ @@ -939,10 +924,12 @@ $posts = Post::find()->with([ ])->all(); ``` -### Default Scope +### デフォルトスコープ -If you used Yii 1.1 before, you may know a concept called *default scope*. A default scope is a scope that -applies to ALL queries. You can define a default scope easily by overriding [[yii\db\ActiveRecord::find()]]. For example, +あなたが Yii 1.1 を前に使ったことがあれば、*デフォルトスコープ* と呼ばれる概念を知っているかも知れません。 +デフォルトスコープは、全てのクエリに適用されるスコープです。 +デフォルトスコープは、[[yii\db\ActiveRecord::find()]] をオーバライドすることによって、簡単に定義することが出来ます。 +例えば、 ```php public static function find() @@ -951,17 +938,15 @@ public static function find() } ``` -Note that all your queries should then not use [[yii\db\ActiveQuery::where()|where()]] but -[[yii\db\ActiveQuery::andWhere()|andWhere()]] and [[yii\db\ActiveQuery::orWhere()|orWhere()]] -to not override the default condition. +ただし、すべてのクエリにおいて、デフォルトの条件を上書きしないために、[[yii\db\ActiveQuery::where()|where()]] を使わず、[[yii\db\ActiveQuery::andWhere()|andWhere()]] または [[yii\db\ActiveQuery::orWhere()|orWhere()]] を使うべきであることに注意してください。 -Transactional operations ---------------------- +トランザクション操作 +-------------------- -There are two ways of dealing with transactions while working with Active Record. First way is doing everything manually -as described in the "transactions" section of "[Database basics](db-dao.md)". Another way is to implement the -`transactions` method where you can specify which operations are to be wrapped into transactions on a per model scenario: +アクティブレコードを扱う際には、二つの方法でトランザクション操作を処理することができます。 +最初の方法は、"[データベースの基礎](db-dao.md)" の「トランザクション」の項で説明したように、全てを手作業でやる方法です。 +もう一つの方法として、`transactions` メソッドを実装して、モデルのシナリオごとに、どの操作をトランザクションで囲むかを指定することが出来ます。 ```php class Post extends \yii\db\ActiveRecord @@ -971,47 +956,45 @@ class Post extends \yii\db\ActiveRecord return [ 'admin' => self::OP_INSERT, 'api' => self::OP_INSERT | self::OP_UPDATE | self::OP_DELETE, - // the above is equivalent to the following: + // 上は次と等値 // 'api' => self::OP_ALL, ]; } } ``` -In the above `admin` and `api` are model scenarios and the constants starting with `OP_` are operations that should -be wrapped in transactions for these scenarios. Supported operations are `OP_INSERT`, `OP_UPDATE` and `OP_DELETE`. -`OP_ALL` stands for all three. +上記において、`admin` と `api` はモデルのシナリオであり、`OP_` で始まる定数は、これらのシナリオについてトランザクションで囲まれるべき操作を示しています。 +サポートされている操作は、`OP_INSERT`、`OP_UPDATE`、そして、`OP_DELETE` です。 +`OP_ALL` は三つ全てを示します。 -Such automatic transactions are especially useful if you're doing additional database changes in `beforeSave`, -`afterSave`, `beforeDelete`, `afterDelete` and want to be sure that both succeeded before they are saved. +このような自動的なトランザクションは、`beforeSave`、`afterSave`、`beforeDelete`、`afterDelete` によってデータベースに追加の変更を加えており、本体の変更と追加の変更の両方が成功した場合にだけデータベースにコミットしたい、というときに取り分けて有用です。 -Optimistic Locks --------------- +楽観的ロック +------------ -Optimistic locking allows multiple users to access the same record for edits and avoids -potential conflicts. For example, when a user attempts to save the record upon some staled data -(because another user has modified the data), a [[\yii\db\StaleObjectException]] exception will be thrown, -and the update or deletion is skipped. +楽観的ロックは、複数のユーザが編集のために同一のレコードにアクセスすることを許容しつつ、発生しうる衝突を回避するものです。 +例えば、ユーザが (別のユーザが先にデータを修正したために) 陳腐化したデータに対してレコードの保存を試みた場合は、[[\yii\db\StaleObjectException]] 例外が投げられて、更新または削除はスキップされます。 -Optimistic locking is only supported by `update()` and `delete()` methods and isn't used by default. +楽観的ロックは、`update()` と `delete()` メソッドだけでサポートされ、既定では使用されません。 -To use Optimistic locking: +楽観的ロックを使用するためには、 -1. Create a column to store the version number of each row. The column type should be `BIGINT DEFAULT 0`. - Override the `optimisticLock()` method to return the name of this column. -2. In the Web form that collects the user input, add a hidden field that stores - the lock version of the recording being updated. -3. In the controller action that does the data updating, try to catch the [[\yii\db\StaleObjectException]] - and implement necessary business logic (e.g. merging the changes, prompting stated data) - to resolve the conflict. +1. 各行のバージョン番号を保存するカラムを作成します。カラムのタイプは `BIGINT DEFAULT 0` でなければなりません。 + `optimisticLock()` メソッドをオーバーライドして、このカラムの名前を返すようにします。 +2. ユーザ入力を収集するウェブフォームに、更新されるレコードのロックバージョンを保持する隠しフィールドを追加します。 +3. データ更新を行うコントローラアクションにおいて、[[\yii\db\StaleObjectException]] 例外を捕捉して、衝突を解決するために必要なビジネスロジック (例えば、変更をマージしたり、データの陳腐化を知らせたり) を実装します。 -Dirty Attributes +ダーティな属性 -------------- -An attribute is considered dirty if its value was modified after the model was loaded from database or since the most recent data save. When saving record data by calling `save()`, `update()`, `insert()` etc. only dirty attributes are saved into the database. If there are no dirty attributes then there is nothing to be saved so no query will be issued at all. +属性は、データベースからロードされた後、または最後のデータ保存の後に値が変更されると、ダーティであると見なされます。 +そして、`save()`、`update()`、`insert()` などを呼んでレコードデータを保存するときは、ダーティな属性だけがデータベースに保存されます。 +ダーティな属性が無い場合は、保存すべきものは無いことになり、クエリは何も発行されません。 -See also --------- +参照 +---- + +以下も参照してください。 -- [Model](structure-models.md) +- [モデル](structure-models.md) - [[yii\db\ActiveRecord]]