Welcome to the Cookbook

loading...

{JA} - 3.7.6.5 hasAndBelongsToMany (HABTM)

さて、この時点で、すでに CakePHP におけるモデルの関連付けの専門家になっていることでしょう。すでに3つの関連に精通し、オブジェクトの関連付けの大半を学んできました。

それでは最後の関連である hasAndBelongsToMany もしくは HABTM に取り組みましょう。この関連が使用されるのは、2つのモデルがあり、それらがさまざまな方法で繰り返し何度も連携する必要がある場合です。

hasMany と HABTM の間の主な違いは、HABTM 内のモデル間の結びつきが排他的ではないということです。たとえば、HABTM を使用して Recipe モデルが Tag モデルと連携するとします。おばあちゃんのニョッキ(訳注:イタリアの伝統料理)レシピに "Italian" というタグを割り当てても、タグを「使い切る」ことにはなりません。蜂蜜でテカテカの BBQ スパゲッティにも、"Italian" とタグ付けできます。

hasMany 関連のオブジェクトの間の結びつきは排他的です。User が Comment と hasMany である場合、コメントは特定のユーザにのみ結び付けられます。あるユーザに結びついたコメントは、もう他のユーザに結びつけることはできません。

先に進めましょう。HABTM 関連を扱うには、追加のテーブルをデータベースにセットアップする必要があります。この新しい追加のテーブルの名前は、両方のモデルの名前が含まれており、それらをアルファベット順に並べてアンダースコア(「_」)で繋げたものにします。テーブルは少なくも2つのフィールドを含み、それぞれの外部キー(integer にすべき)が各モデルの主キーである必要があります。問題を避けるために、これら2つのフィールドを複合主キーにしないでください。アプリケーションにおいてそうする必要がある場合は、ユニークなインデックスを定義します。このテーブルに何か情報を追加する場合は、他のモデルと同じように簡単に扱えるよう、主キーのフィールド(規約上は「id」という名前のフィールド)を追加するとよいでしょう。

HABTM 両方のモデル名を含んだテーブルを追加する必要があります

関係 スキーマ
Recipe HABTM Tag id, recipes_tags.recipe_id, recipes_tags.tag_id
Cake HABTM Fan id, cakes_fans.cake_id, cakes_fans.fan_id
Foo HABTM Bar id, bars_foos.foo_id, bars_foos.bar_id

テーブル名は規約によりアルファベット順です。

新しいテーブルが作成されると、モデルのファイルに HABTM 関連を定義できます。ここでは、文字列による定義ではなく、配列の構文を使いましょう:

<?php

class Recipe extends AppModel {
    var $name = 'Recipe';   
    var $hasAndBelongsToMany = array(
        'Tag' =>
            array(
                 'className'              => 'Tag',
                 'joinTable'              => 'recipes_tags',
                 'with'                   => '',
                'foreignKey'             => 'recipe_id',
                'associationForeignKey'  => 'tag_id',
                'unique'                 => true,
                'conditions'             => '',
                'fields'                 => '',
                'order'                  => '',
                'limit'                  => '',
                'offset'                 => '',
                'finderQuery'            => '',
                'deleteQuery'            => '',
                'insertQuery'            => ''
            )
    );
}
?>
  1. <?php
  2. class Recipe extends AppModel {
  3. var $name = 'Recipe';
  4. var $hasAndBelongsToMany = array(
  5. 'Tag' =>
  6. array(
  7. 'className' => 'Tag',
  8. 'joinTable' => 'recipes_tags',
  9. 'with' => '',
  10. 'foreignKey' => 'recipe_id',
  11. 'associationForeignKey' => 'tag_id',
  12. 'unique' => true,
  13. 'conditions' => '',
  14. 'fields' => '',
  15. 'order' => '',
  16. 'limit' => '',
  17. 'offset' => '',
  18. 'finderQuery' => '',
  19. 'deleteQuery' => '',
  20. 'insertQuery' => ''
  21. )
  22. );
  23. }
  24. ?>

HABTM 関連の配列で指定可能なキーは次の通りです:

  • className: 現在のモデルに関連したモデルのクラス名。‘Recipe HABTM Tag’ という関係を定義する場合、className キーは‘Tag’ になります。
  • joinTable: 関連で使用する結合テーブルの名前(もし HBTM の結合テーブル名が規約に従っていない場合)
  • with: 結合テーブルのモデル名の定義。デフォルトでは CakePHP は自動的にモデルを生成します。上述の例だと、RecipesTag が呼び出されます。デフォルトの名前を上書きするために、このキーを使います。結合テーブルのモデルは、あらゆる「通常の」モデルのように、結合テーブルへアクセスするために使用することができます。
  • foreignKey: 現在のモデルにある外部キーの名前。複数の HABTM 関係を定義する必要がある場合に特に便利です。このキーのデフォルト値は、現在のモデル名のアンダースコア区切りの単数形で、末尾に‘_id’をつけたものです。
  • associationForeignKey: もう一方のモデルにある外部キーの名前。複数の HABTM 関係を定義する必要がある場合に特に便利です。このキーのデフォルト値は、もう一方のモデル名のアンダースコア区切りの単数形で、末尾に‘_id’をつけたものです。
  • unique: もし true(デフォルト)なら、Cake は更新を行う際、外部キーのテーブルに新たなレコードを挿入する前に既存の関連レコードを削除します。したがって、更新を行う際には、既存の関連するレコードをもう一度渡す必要があります。
  • conditions: 関連モデルのレコードを限定するための SQL。SQL 内でモデル名を使用することを習慣にしておくようにしておきましょう:“status = 1.” よりも、“Comment.status = 1” の方が良い記述です。
  • fields: 関連モデルのデータが取得された際に取り出すフィールドのリストです。デフォルトではすべてのフィールドを返します。
  • order: 返される関連する行の並び順を定義する SQL。.
  • limit: 返して欲しい関連する行の最大数。
  • offset: 与えられた現在の条件と順番で関連したモデルのレコードを取り出す時に、スキップする行の数。
  • finderQuery, deleteQuery, insertQuery: 関連モデルのレコードを取得・削除・生成するために CakePHP が使用できる完全な SQL。これは独自の結果が必要な場合に使用します。

この関連を定義すると、Recipe モデルの find は、関連した Tag モデルのレコードも(もし存在すれば)取り出します:

// $this->Recipe->find() を呼び出した結果のサンプルArray
(  
    [Recipe] => Array
        (
            [id] => 2745
            [name] => Chocolate Frosted Sugar Bombs
            [created] => 2007-05-01 10:31:01
            [user_id] => 2346
        )
    [Tag] => Array
        (
            [0] => Array
                (
                    [id] => 123
                    [name] => Breakfast
                )
           [1] => Array
                (
                    [id] => 124
                    [name] => Dessert
                )
           [2] => Array
                (
                    [id] => 125
                    [name] => Heart Disease
                )
        )
)

Tag モデルを使用する際に Recipe データを取得したい場合は、Tag モデルに HABTM の関連を定義することを覚えておいてください。

HABTM 関連に基づいた独自の find クエリを実行することもできます。次の例をみてください:

上記の例と同じ構造(Recipe HABTM Tag)を仮定し、'Dessert' タグをもつすべての Recipe を取得したいとします。これを達成できる一つの方法(ただし悪い方法)は、アソシエーションそのものに検索する条件を適用することです:

$this->Recipe->bindModel(array(
	'hasAndBelongsToMany' => array(
		'Tag' => array('conditions'=>array('Tag.name'=>'Dessert'))
)));
$this->Recipe->find('all');
  1. $this->Recipe->bindModel(array(
  2. 'hasAndBelongsToMany' => array(
  3. 'Tag' => array('conditions'=>array('Tag.name'=>'Dessert'))
  4. )));
  5. $this->Recipe->find('all');
//Data Returned
Array
(  
    0 => Array
        {
        [Recipe] => Array
            (
                [id] => 2745
                [name] => Chocolate Frosted Sugar Bombs
                [created] => 2007-05-01 10:31:01
                [user_id] => 2346
            )
        [Tag] => Array
            (
               [0] => Array
                    (
                        [id] => 124
                        [name] => Dessert
                    )
            )
    )
    1 => Array
        {
        [Recipe] => Array
            (
                [id] => 2745
                [name] => Crab Cakes
                [created] => 2008-05-01 10:31:01
                [user_id] => 2349
            )
        [Tag] => Array
            (
            }
        }
}

この例では "Dessert" タグがついた全てのレシピしか返せないことに注意してください。これをきちんと達成するには、いくつかの方法があります。一つの方法は、Recipe ではなく Tag モデルを検索し、関連付いた Recipe も全て取得する方法です。

$this->Recipe->Tag->find('all', array('conditions'=>array('Tag.name'=>'Dessert')));
  1. $this->Recipe->Tag->find('all', array('conditions'=>array('Tag.name'=>'Dessert')));

与えられた ID を検索するために、CakePHP が提供する結合テーブルのモデルを使うことも出来ます。

$this->Recipe->bindModel(array('hasOne' => array('RecipesTag')));
$this->Recipe->find('all', array(
		'fields' => array('Recipe.*'),
		'conditions'=>array('RecipesTag.tag_id'=>124) // id of Dessert
));
  1. $this->Recipe->bindModel(array('hasOne' => array('RecipesTag')));
  2. $this->Recipe->find('all', array(
  3. 'fields' => array('Recipe.*'),
  4. 'conditions'=>array('RecipesTag.tag_id'=>124) // id of Dessert
  5. ));

フィルタリングを行うために必要な数の結合を生成するために、風変わりな関連付けを作成することもできます。例を見てください:

$this->Recipe->bindModel(array(
	'hasOne' => array(
		'RecipesTag',
		'FilterTag' => array(
			'className' => 'Tag',
			'foreignKey' => false,
			'conditions' => array('FilterTag.id = RecipesTag.tag_id')
))));
$this->Recipe->find('all', array(
		'fields' => array('Recipe.*'),
		'conditions'=>array('FilterTag.name'=>'Dessert')
));
  1. $this->Recipe->bindModel(array(
  2. 'hasOne' => array(
  3. 'RecipesTag',
  4. 'FilterTag' => array(
  5. 'className' => 'Tag',
  6. 'foreignKey' => false,
  7. 'conditions' => array('FilterTag.id = RecipesTag.tag_id')
  8. ))));
  9. $this->Recipe->find('all', array(
  10. 'fields' => array('Recipe.*'),
  11. 'conditions'=>array('FilterTag.name'=>'Dessert')
  12. ));

両方の例は、次のデータを返します:

//Data Returned
Array
(  
    0 => Array
        {
        [Recipe] => Array
            (
                [id] => 2745
                [name] => Chocolate Frosted Sugar Bombs
                [created] => 2007-05-01 10:31:01
                [user_id] => 2346
            )
    [Tag] => Array
        (
            [0] => Array
                (
                    [id] => 123
                    [name] => Breakfast
                )
           [1] => Array
                (
                    [id] => 124
                    [name] => Dessert
                )
           [2] => Array
                (
                    [id] => 125
                    [name] => Heart Disease
                )
        )
}

同じようなバインディングトリックで、 HABTM モデルのページ付けを簡単にすることができます。一点だけ注意が必要です。ページ付けで2つのクエリ(レコードの数を数えるものと、実際のデータを取得するもの)が必要な場合、必ず bindModel();false パラメータをセットしてください。こうすることで、デフォルトのビヘイビアのように、単一ではなく複数のクエリにまたがってモデルの関連が維持されます。詳細は API に関する文書を参照してください。

アソシエーションをその場で取り扱うことについての詳しい情報は、その場でアソシエーションを生成、廃棄の章を参照してください。

その時々の目的に応じて、これらのテクニックを組み合わせたり適用してください。

{EN} - 3.7.6.5 hasAndBelongsToMany (HABTM)

Alright. At this point, you can already call yourself a CakePHP model associations professional. You're already well versed in the three associations that take up the bulk of object relations.

Let's tackle the final relationship type: hasAndBelongsToMany, or HABTM. This association is used when you have two models that need to be joined up, repeatedly, many times, in many different ways.

The main difference between hasMany and HABTM is that a link between models in HABTM is not exclusive. For example, we're about to join up our Recipe model with a Tag model using HABTM. Attaching the "Italian" tag to my grandma's Gnocci recipe doesn't "use up" the tag. I can also tag my Honey Glazed BBQ Spaghettio's with "Italian" if I want to.

Links between hasMany associated objects are exclusive. If my User hasMany Comments, a comment is only linked to a specific user. It's no longer up for grabs.

Moving on. We'll need to set up an extra table in the database to handle HABTM associations. This new join table's name needs to include the names of both models involved, in alphabetical order, and separated with an underscore ( _ ). The contents of the table should be two fields, each foreign keys (which should be integers) pointing to both of the primary keys of the involved models. To avoid any issues - don't define a combined primary key for these two fields, if your application requires it you can define a unique index. If you plan to add any extra information to this table, it's a good idea to add an additional primary key field (by convention 'id') to make acting on the table as easy as any other model.

HABTM requires a separate join table that includes both model names.

Relation Schema (HABTM table in bold)
Recipe HABTM Tag recipes_tags.id, recipes_tags.recipe_id, recipes_tags.tag_id
Cake HABTM Fan cakes_fans.id, cakes_fans.cake_id, cakes_fans.fan_id
Foo HABTM Bar bars_foos.id, bars_foos.foo_id, bars_foos.bar_id

Table names are by convention in alphabetical order.

Make sure primary keys in tables cakes and recipes have "id" fields as assumed by convention. If they're different than assumed, it has to be changed in model

Once this new table has been created, we can define the HABTM association in the model files. We're gonna skip straight to the array syntax this time:

<?php

class Recipe extends AppModel {
    var $name = 'Recipe';   
    var $hasAndBelongsToMany = array(
        'Tag' =>
            array(
                'className'              => 'Tag',
                'joinTable'              => 'recipes_tags',
                'foreignKey'             => 'recipe_id',
                'associationForeignKey'  => 'tag_id',
                'unique'                 => true,
                'conditions'             => '',
                'fields'                 => '',
                'order'                  => '',
                'limit'                  => '',
                'offset'                 => '',
                'finderQuery'            => '',
                'deleteQuery'            => '',
                'insertQuery'            => ''
            )
    );
}
?>
  1. <?php
  2. class Recipe extends AppModel {
  3. var $name = 'Recipe';
  4. var $hasAndBelongsToMany = array(
  5. 'Tag' =>
  6. array(
  7. 'className' => 'Tag',
  8. 'joinTable' => 'recipes_tags',
  9. 'foreignKey' => 'recipe_id',
  10. 'associationForeignKey' => 'tag_id',
  11. 'unique' => true,
  12. 'conditions' => '',
  13. 'fields' => '',
  14. 'order' => '',
  15. 'limit' => '',
  16. 'offset' => '',
  17. 'finderQuery' => '',
  18. 'deleteQuery' => '',
  19. 'insertQuery' => ''
  20. )
  21. );
  22. }
  23. ?>

Possible keys for HABTM association arrays include:

  • className: the classname of the model being associated to the current model. If you're defining a ‘Recipe HABTM Tag' relationship, the className key should equal ‘Tag.'
  • joinTable: The name of the join table used in this association (if the current table doesn't adhere to the naming convention for HABTM join tables).
  • with: Defines the name of the model for the join table. By default CakePHP will auto-create a model for you. Using the example above it would be called RecipesTag. By using this key you can override this default name. The join table model can be used just like any "regular" model to access the join table directly.
  • foreignKey: the name of the foreign key found in the current model. This is especially handy if you need to define multiple HABTM relationships. The default value for this key is the underscored, singular name of the current model, suffixed with ‘_id'.
  • associationForeignKey: the name of the foreign key found in the other model. This is especially handy if you need to define multiple HABTM relationships. The default value for this key is the underscored, singular name of the other model, suffixed with ‘_id'.
  • unique: If true (default value) cake will first delete existing relationship records in the foreign keys table before inserting new ones, when updating a record. So existing associations need to be passed again when updating.
  • conditions: An SQL fragment used to filter related model records. It's good practice to use model names in SQL fragments: "Comment.status = 1" is always better than just "status = 1."
  • fields: A list of fields to be retrieved when the associated model data is fetched. Returns all fields by default.
  • order: An SQL fragment that defines the sorting order for the returned associated rows.
  • limit: The maximum number of associated rows you want returned.
  • offset: The number of associated rows to skip over (given the current conditions and order) before fetching and associating.
  • finderQuery, deleteQuery, insertQuery: A complete SQL query CakePHP can use to fetch, delete, or create new associated model records. This should be used in situations that require very custom results.

Once this association has been defined, find operations on the Recipe model will also fetch related Tag records if they exist:

//Sample results from a $this->Recipe->find() call.

Array
(  
    [Recipe] => Array
        (
            [id] => 2745
            [name] => Chocolate Frosted Sugar Bombs
            [created] => 2007-05-01 10:31:01
            [user_id] => 2346
        )
    [Tag] => Array
        (
            [0] => Array
                (
                    [id] => 123
                    [name] => Breakfast
                )
           [1] => Array
                (
                    [id] => 124
                    [name] => Dessert
                )
           [2] => Array
                (
                    [id] => 125
                    [name] => Heart Disease
                )
        )
)

Remember to define a HABTM association in the Tag model if you'd like to fetch Recipe data when using the Tag model.

It is also possible to execute custom find queries based on HABTM relationships. Consider the following examples:

Assuming the same structure in the above example (Recipe HABTM Tag), let's say we want to fetch all Recipes with the tag 'Dessert', one potential (wrong) way to achieve this would be to apply a condition to the association itself:

$this->Recipe->bindModel(array(
	'hasAndBelongsToMany' => array(
		'Tag' => array('conditions'=>array('Tag.name'=>'Dessert'))
)));
$this->Recipe->find('all');
  1. $this->Recipe->bindModel(array(
  2. 'hasAndBelongsToMany' => array(
  3. 'Tag' => array('conditions'=>array('Tag.name'=>'Dessert'))
  4. )));
  5. $this->Recipe->find('all');
//Data Returned
Array
(  
    0 => Array
        {
        [Recipe] => Array
            (
                [id] => 2745
                [name] => Chocolate Frosted Sugar Bombs
                [created] => 2007-05-01 10:31:01
                [user_id] => 2346
            )
        [Tag] => Array
            (
               [0] => Array
                    (
                        [id] => 124
                        [name] => Dessert
                    )
            )
    )
    1 => Array
        {
        [Recipe] => Array
            (
                [id] => 2745
                [name] => Crab Cakes
                [created] => 2008-05-01 10:31:01
                [user_id] => 2349
            )
        [Tag] => Array
            (
            }
        }
}

Notice that this example returns ALL recipes but only the "Dessert" tags. To properly achieve our goal, there are a number of ways to do it. One option is to search the Tag model (instead of Recipe), which will also give us all of the associated Recipes.

$this->Recipe->Tag->find('all', array('conditions'=>array('Tag.name'=>'Dessert')));
  1. $this->Recipe->Tag->find('all', array('conditions'=>array('Tag.name'=>'Dessert')));

We could also use the join table model (which CakePHP provides for us), to search for a given ID.

$this->Recipe->bindModel(array('hasOne' => array('RecipesTag')));
$this->Recipe->find('all', array(
		'fields' => array('Recipe.*'),
		'conditions'=>array('RecipesTag.tag_id'=>124) // id of Dessert
));
  1. $this->Recipe->bindModel(array('hasOne' => array('RecipesTag')));
  2. $this->Recipe->find('all', array(
  3. 'fields' => array('Recipe.*'),
  4. 'conditions'=>array('RecipesTag.tag_id'=>124) // id of Dessert
  5. ));

It's also possible to create an exotic association for the purpose of creating as many joins as necessary to allow filtering, for example:

$this->Recipe->bindModel(array(
	'hasOne' => array(
		'RecipesTag',
		'FilterTag' => array(
			'className' => 'Tag',
			'foreignKey' => false,
			'conditions' => array('FilterTag.id = RecipesTag.tag_id')
))));
$this->Recipe->find('all', array(
		'fields' => array('Recipe.*'),
		'conditions'=>array('FilterTag.name'=>'Dessert')
));
  1. $this->Recipe->bindModel(array(
  2. 'hasOne' => array(
  3. 'RecipesTag',
  4. 'FilterTag' => array(
  5. 'className' => 'Tag',
  6. 'foreignKey' => false,
  7. 'conditions' => array('FilterTag.id = RecipesTag.tag_id')
  8. ))));
  9. $this->Recipe->find('all', array(
  10. 'fields' => array('Recipe.*'),
  11. 'conditions'=>array('FilterTag.name'=>'Dessert')
  12. ));

Both of which will return the following data:

//Data Returned
Array
(  
    0 => Array
        {
        [Recipe] => Array
            (
                [id] => 2745
                [name] => Chocolate Frosted Sugar Bombs
                [created] => 2007-05-01 10:31:01
                [user_id] => 2346
            )
    [Tag] => Array
        (
            [0] => Array
                (
                    [id] => 123
                    [name] => Breakfast
                )
           [1] => Array
                (
                    [id] => 124
                    [name] => Dessert
                )
           [2] => Array
                (
                    [id] => 125
                    [name] => Heart Disease
                )
        )
}

The same binding trick can be used to easily paginate your HABTM models. Just one word of caution: since paginate requires two queries (one to count the records and one to get the actual data), be sure to supply the false parameter to your bindModel(); which essentially tells CakePHP to keep the binding persistent over multiple queries, rather than just one as in the default behavior. Please refer to the API for more details.

For more information on saving HABTM objects see Saving Related Model Data (HABTM)

For more information on binding model associations on the fly see Creating and destroying associations on the fly

Mix and match techniques to achieve your specific objective.

差分

Lines: 1-47Lines: 1-43
 <title>hasAndBelongsToMany (HABTM)</title> <title>hasAndBelongsToMany (HABTM)</title>
-<p>Alright. At this point, you can already call yourself a CakePHP model associations professional. You�39;re already well versed in the three associations that take up the bulk of object relations.</p>
<p>Let's tackle the final relationship type: hasAndBelongsToMany, or HABTM. This association is used when you have two models that need to be joined up, repeatedly, many times, in many different ways.</p>
<p>The main difference between hasMany and HABTM is that a link between models in HABTM is not exclusive. For example, we're about to join up our Recipe model with a Tag model using HABTM. Attaching the "Italian" tag to my grandma's Gnocci recipe doesn't "use up" the tag. I can also tag my Honey Glazed BBQ Spaghettio's with "Italian" if I want to.</p>
<p>Links between hasMany associated objects are exclusive. If my User hasMany Comments, a comment is only linked to a specific user. It's no longer up for grabs.</p>
<p>Moving on. We'll need to set up an extra table in the database to handle HABTM associations. This new join table's name needs to include the names of both models involved, in alphabetical order, and separated with an underscore ( _ ). The contents of the table should be two fields, each foreign keys (which should be integers) pointing to both of the primary keys of the involved models. To avoid any issues - don't define a combined primary key for these two fields, if your application requires it you can define a unique index. If you plan to add any extra information to this table, it's a good idea to add an additional primary key field (by convention 'id') to make acting on the table as easy as any other model.</p>
+<p>さて、この時点で、すでに CakePHP におけるモデルの関連付けの専門家になっていることでしょう。すでに3つの関連に精通し、オブジェクトの関連付けの大半を学んできました。</p>
<p>それでは最後の関連である hasAndBelongsToMany もしくは HABTM に取り組みましょう。この関連が使用されるのは、2つのモデルがあり、それらがさまざまな方法で繰り返し何度も連携する必要がある場合です。</p>
<p>hasMany HABTM の間の主な違いは、HABTM 内のモデル間の結びつきが排他的ではないということです。たとえば、HABTM を使用して Recipe モデルが Tag モデルと連携するとします。おばあちゃんのニョッキ(訳注:イタリアの伝統料理)レシピに "Italian" というタグを割り当てても、タグを「使い切る」ことにはなりません。蜂蜜でテカテカの BBQ スパゲッティにも、"Italian" とタグ付けできます。 </p>
<p>hasMany 関連のオブジェクトの間の結びつきは排他的です。User Comment と hasMany である場合、コメントは特定のユーザにのみ結び付けられます。あるユーザに結びついたコメントは、もう他のユーザに結びつけることはできません。</p>
<p>先に進めましょう。HABTM 関連を扱うには、追加のテーブルをデータベースにセットアップする必要があります。この新しい追加のテーブルの名前は、両方のモデルの名前が含まれており、それらをアルファベット順に並べてアンダースコア(_)で繋げたものにします。テーブルは少なくも2つのフィールドを含み、それぞれの外部キー(integer にすべき)が各モデルの主キーである必要があります。問題を避けるために、これら2つのフィールドを複合主キーにしないでください。アプリケーションにおいてそうする必要がある場合は、ユニークなインデックスを定義します。このテーブルに何か情報を追加する場合は、他のモデルと同じように簡単に扱えるよう、主キーのフィールド(規約上は「id」という名前のフィールド)を追加するとよいでしょう。</p>
 <p> <p>
-<strong>HABTM</strong> requires a separate join table that includes both <em>model</em> names. </p> +<strong>HABTM</strong> 両方の<em>モデル</em>名を含んだテーブルを追加する必要があります</p>
 <table> <table>
 <tr> <tr>
-<th>Relation</th>
<th>Schema (HABTM table in bold)</th>
+<th>関係</th>
<th>スキーマ</th>
 </tr> </tr>
 <tr> <tr>
 <td>Recipe HABTM Tag</td> <td>Recipe HABTM Tag</td>
-<td>
<b>recipes_tags.</b
>id, <b>recipes_tags.</b>recipe_id, <b>recipes_tags.</b>tag_id</td>
+<td>id, recipes_tags.recipe_id, recipes_tags.tag_id</td>
 </tr> </tr>
 <tr> <tr>
 <td>Cake HABTM Fan</td> <td>Cake HABTM Fan</td>
-<td>
<b>cakes_fans.</b
>id, <b>cakes_fans.</b>cake_id, <b>cakes_fans.</b>fan_id</td>
+<td>id, cakes_fans.cake_id, cakes_fans.fan_id</td>
 </tr> </tr>
 <tr> <tr>
 <td>Foo HABTM Bar</td> <td>Foo HABTM Bar</td>
-<td>
<b>bars_foos.</b
>id, <b>bars_foos.</b>foo_id, <b>bars_foos.</b>bar_id</td>
+<td>id, bars_foos.foo_id, bars_foos.bar_id</td>
 </tr> </tr>
 </table> </table>
-<p class="note">Table names are by convention in alphabetical order.</p>
<p>Make sure primary keys in tables <strong>cakes</strong> and <strong>recipes</strong> have "id" fields as assumed by convention. If they're different than assumed, it <a href="/view/437/primaryKey"> has to be changed in model</a>
</p>
<p>Once this new table has been created, we can define the
HABTM association in the model files. We're gonna skip straight to the array syntax this time:</p>
+<p class="note">テーブル名は規約によりアルファベット順です。</p>
<p>新しいテーブルが作成されると、モデルのファイルに HABTM 関連を定義できます。ここでは、文字列による定義ではなく、配列の構文を使いましょう: </p>
 <pre> <pre>
 &lt;?php &lt;?php
 class Recipe extends AppModel { class Recipe extends AppModel {
  var $name = 'Recipe';   var $name = 'Recipe';
  var $hasAndBelongsToMany = array(  var $hasAndBelongsToMany = array(
  'Tag' =&gt;  'Tag' =&gt;
  array(  array(
- 'className' =&gt; 'Tag',
'joinTable' =&gt; 'recipes_tags',
+ 'className' =&gt; 'Tag',
'joinTable' =&gt; 'recipes_tags',
'with' =&gt; '
',
  'foreignKey' =&gt; 'recipe_id',  'foreignKey' =&gt; 'recipe_id',
  'associationForeignKey' =&gt; 'tag_id',  'associationForeignKey' =&gt; 'tag_id',
  'unique' =&gt; true,  'unique' =&gt; true,
  'conditions' =&gt; '',  'conditions' =&gt; '',
Lines: 56-107Lines: 52-89
  );  );
 } }
 ?&gt; ?&gt;
 </pre> </pre>
-<p>Possible keys for HABTM association arrays include:</p> +<p>HABTM 関連の配列で指定可能なキーは次の通りです:</p>
 <ul> <ul>
 <li> <li>
-<strong>className</strong>: the classname of the model being associated to the current model. If you're defining a ‘Recipe HABTM Tag' relationship, the className key should equal ‘Tag.' /></li> +<strong>className</strong>: 現在のモデルに関連したモデルのクラス名。‘Recipe HABTM Tag という関係を定義する場合、className キーは‘Tag’ になります。 </li>
 <li> <li>
-<strong>joinTable</strong>: The name of the join table used in this association (if the current table doesn't adhere to the naming convention for HABTM join tables).
</li>
+<strong>joinTable</strong>: 関連で使用する結合テーブルの名前(もし HBTM の結合テーブル名が規約に従っていない場合)</li>
 <li> <li>
-<strong>with</strong>: Defines the name of the model for the join table. By default CakePHP will auto-create a model for you. Using the example above it would be called RecipesTag. By using this key you can override this default name. The join table model can be used just like any "regular" model to access the join table directly. /></li> +<strong>with</strong>: 結合テーブルのモデル名の定義。デフォルトでは CakePHP は自動的にモデルを生成します。上述の例だと、RecipesTag が呼び出されます。デフォルトの名前を上書きするために、このキーを使います。結合テーブルのモデルは、あらゆる「通常の」モデルのように、結合テーブルへアクセスするために使用することができます。</li>
 <li> <li>
-<strong>foreignKey</strong>: the name of the foreign key found in the current model. This is especially handy if you need to define multiple HABTM relationships. The default value for this key is the underscored, singular name of the current model, suffixed with ‘_id'.
</li>
+<strong>foreignKey</strong>: 現在のモデルにある外部キーの名前。複数の HABTM 関係を定義する必要がある場合に特に便利です。このキーのデフォルト値は、現在のモデル名のアンダースコア区切りの単数形で、末尾に‘_id’をつけたものです。</li>
 <li> <li>
-<strong>associationForeignKey</strong>: the name of the foreign key found in the other model. This is especially handy if you need to define multiple HABTM relationships. The default value for this key is the underscored, singular name of the other model, suffixed with ‘_id'.
</li>
+<strong>associationForeignKey</strong>: もう一方のモデルにある外部キーの名前。複数の HABTM 関係を定義する必要がある場合に特に便利です。このキーのデフォルト値は、もう一方のモデル名のアンダースコア区切りの単数形で、末尾に‘_id’をつけたものです。</li>
 <li> <li>
-<strong>unique</strong>: If true (default value) cake will first delete existing relationship records in the foreign keys table before inserting new ones, when updating a record. So existing associations need to be passed again when updating.
</li>
+<strong>unique</strong>: もし true(デフォルト)なら、Cake は更新を行う際、外部キーのテーブルに新たなレコードを挿入する前に既存の関連レコードを削除します。したがって、更新を行う際には、既存の関連するレコードをもう一度渡す必要があります。</li>
 <li> <li>
-<strong>conditions</strong>: An SQL fragment used to filter related model records. It's good practice to use model names in SQL fragments: "Comment.status = 1&quot; is always better than just "status = 1." /></li> +<strong>conditions</strong>: 関連モデルのレコードを限定するための SQLSQL 内でモデル名を使用することを習慣にしておくようにしておきましょう:status = 1.” よりも、“Comment.status = 1 の方が良い記述です。</li>
 <li> <li>
-<strong>fields</strong>: A list of fields to be retrieved when the associated model data is fetched. Returns all fields by default.
</li>
+<strong>fields</strong>: 関連モデルのデータが取得された際に取り出すフィールドのリストです。デフォルトではすべてのフィールドを返します。 </li>
 <li> <li>
-<strong>order</strong>: An SQL fragment that defines the sorting order for the returned associated rows.
</li>
+<strong>order</strong>: 返される関連する行の並び順を定義する SQL.</li>
 <li> <li>
-<strong>limit</strong>: The maximum number of associated rows you want returned.
</li>
+<strong>limit</strong>: 返して欲しい関連する行の最大数。</li>
 <li> <li>
-<strong>offset</strong>: The number of associated rows to skip over (given the current conditions and order) before fetching and associating.
</li>
+<strong>offset</strong>: 与えられた現在の条件と順番で関連したモデルのレコードを取り出す時に、スキップする行の数。</li>
 <li> <li>
-<strong>finderQuery, deleteQuery, insertQuery</strong>: A complete SQL query CakePHP can use to fetch, delete, or create new associated model records. This should be used in situations that require very custom results.
</li>
+<strong>finderQuery, deleteQuery, insertQuery</strong>: 関連モデルのレコードを取得・削除・生成するために CakePHP が使用できる完全な SQL。これは独自の結果が必要な場合に使用します。</li>
 </ul> </ul>
-<p>Once this association has been defined, find operations on the Recipe model will also fetch related Tag records if they exist:</p> +<p>この関連を定義すると、Recipe モデルの find は、関連した Tag モデルのレコードも(もし存在すれば)取り出します:</p>
 <pre class="plain"> <pre class="plain">
-//Sample results from a $this-&gt;Recipe-&gt;find() call.

Array
+// $this-&gt;Recipe-&gt;find() を呼び出した結果のサンプルArray
 (  (
  [Recipe] =&gt; Array  [Recipe] =&gt; Array
  (  (
  [id] =&gt; 2745  [id] =&gt; 2745
Lines: 128-138Lines: 110-120
  )  )
  )  )
 ) )
 </pre> </pre>
-<p>Remember to define a HABTM association in the Tag model if you'd like to fetch Recipe data when using the Tag model.</p>
<p>It is also possible to execute custom find queries based on HABTM relationships. Consider the following examples:<p>
<p>Assuming the same structure in the above example (Recipe HABTM Tag), let's say we want to fetch all Recipes with the tag 'Dessert', one potential (wrong) way to achieve this would be to apply a condition to the association itself:</p>
+<p>Tag モデルを使用する際に Recipe データを取得したい場合は、Tag モデルに HABTM の関連を定義することを覚えておいてください。</p>
<p>HABTM 関連に基づいた独自の find クエリを実行することもできます。次の例をみてください:<p>
<p>上記の例と同じ構造(Recipe HABTM Tag)を仮定し、'Dessert' タグをもつすべての Recipe を取得したいとします。これを達成できる一つの方法(ただし悪い方法)は、アソシエーションそのものに検索する条件を適用することです:</p>
 <pre> <pre>
 $this-&gt;Recipe-&gt;bindModel(array( $this-&gt;Recipe-&gt;bindModel(array(
  'hasAndBelongsToMany' =&gt; array(  'hasAndBelongsToMany' =&gt; array(
  'Tag' =&gt; array('conditions'=&gt;array('Tag.name'=&gt;'Dessert'))  'Tag' =&gt; array('conditions'=&gt;array('Tag.name'=&gt;'Dessert'))
Lines: 175-199Lines: 157-177
  }  }
  }  }
 } }
 </pre> </pre>
-<p>Notice that this example returns ALL recipes but only the "Dessert" tags. To properly achieve our goal, there are a number of ways to do it.
One o
ption is to search the Tag model (instead of Recipe), which will also give us all of the associated Recipes.</p>
+<p>この例では "Dessert" タグがついた全てのレシピしか返せないことに注意してください。これをきちんと達成するには、いくつかの方法があります。一つの方法は、Recipe ではなく Tag モデルを検索し、関連付いた Recipe も全て取得する方法です。</p>
 <pre> <pre>
 $this-&gt;Recipe-&gt;Tag-&gt;find('all', array('conditions'=&gt;array('Tag.name'=&gt;'Dessert'))); $this-&gt;Recipe-&gt;Tag-&gt;find('all', array('conditions'=&gt;array('Tag.name'=&gt;'Dessert')));
 </pre> </pre>
-<p> />
We could also use the join table model (which
CakePHP provides for us), to search for a given ID.
</p>
+<p>与えられた ID を検索するために、CakePHP が提供する結合テーブルのモデルを使うことも出来ます。</p>
 <pre> <pre>
 $this-&gt;Recipe-&gt;bindModel(array('hasOne' =&gt; array('RecipesTag'))); $this-&gt;Recipe-&gt;bindModel(array('hasOne' =&gt; array('RecipesTag')));
 $this-&gt;Recipe-&gt;find('all', array( $this-&gt;Recipe-&gt;find('all', array(
  'fields' =&gt; array('Recipe.*'),  'fields' =&gt; array('Recipe.*'),
  'conditions'=&gt;array('RecipesTag.tag_id'=&gt;124) // id of Dessert  'conditions'=&gt;array('RecipesTag.tag_id'=&gt;124) // id of Dessert
 )); ));
 </pre> </pre>
-<p> It's also possible to create an exotic association for the purpose of creating as many joins as necessary to allow filtering, for example:</p> +<p>フィルタリングを行うために必要な数の結合を生成するために、風変わりな関連付けを作成することもできます。例を見てください:</p>
 <pre> <pre>
 $this-&gt;Recipe-&gt;bindModel(array( $this-&gt;Recipe-&gt;bindModel(array(
  'hasOne' =&gt; array(  'hasOne' =&gt; array(
  'RecipesTag',  'RecipesTag',
Lines: 206-214Lines: 184-192
  'fields' =&gt; array('Recipe.*'),  'fields' =&gt; array('Recipe.*'),
  'conditions'=&gt;array('FilterTag.name'=&gt;'Dessert')  'conditions'=&gt;array('FilterTag.name'=&gt;'Dessert')
 )); ));
 </pre> </pre>
-<p>Both of which will return the following data:</p> +<p>両方の例は、次のデータを返します:</p>
 <pre class="plain"> <pre class="plain">
 //Data Returned //Data Returned
 Array Array
 (  (
Lines: 240-251Lines: 218-224
  )  )
  )  )
 } }
 </pre> </pre>
-<p>
The same binding trick can be used to easily paginate your
HABTM models. Just one word of caution: since paginate requires two queries (one to count the records and one to get the actual data), be sure to supply the <code>false</code> parameter to your <code>bindModel();</code> which essentially tells CakePHP to keep the binding persistent over multiple queries, rather than just one as in the default behavior. Please refer to the API for more details.
</p>
<p
class=&quot;note">For more information on saving HABTM objects see &lt;a href=&quot;/view/85/Saving-Related-Model-Data-HABTM">Saving Related Model Data (HABTM)</a> /></p>
<p class="note">For more information on binding model associations on the fly see <a href="/view/86/creating-and-destroying-associations-on-the-fly">Creating and destroying associations on the fly</a>
</p>
<p>Mix and match techniques to achieve your specific objective.</p>
+<p>同じようなバインディングトリックで、 HABTM モデルのページ付けを簡単にすることができます。一点だけ注意が必要です。ページ付けで2つのクエリ(レコードの数を数えるものと、実際のデータを取得するもの)が必要な場合、必ず <code>bindModel();</code> <code>false</code> パラメータをセットしてください。こうすることで、デフォルトのビヘイビアのように、単一ではなく複数のクエリにまたがってモデルの関連が維持されます。詳細は API に関する文書を参照してください。</p>
<p class="note">アソシエーションをその場で取り扱うことについての詳しい情報は、<a href="/ja/view/86/creating-and-destroying-associations-on-the-fly">その場でアソシエーションを生成、廃棄</a>の章を参照してください。</p>
<p>その時々の目的に応じて、これらのテクニックを組み合わせたり適用してください。</p>