Yii2:定义多个表之间关系的正确方法是什么?
Yii2: What is the correct way to define relationships among multiple tables?
在控制器中我有以下代码:
public function actionView($id)
{
$query = new Query;
$query->select('*')
->from('table_1 t1')
->innerJoin('table_2 t2', 't2.t1_id = t1.id')
->innerJoin('table_3 t3', 't2.t3_id = t3.id')
->innerJoin('table_4 t4', 't3.t4_id = t4.id')
->andWhere('t1.id = ' . $id);
$rows = $query->all();
return $this->render('view', [
'model' => $this->findModel($id),
'rows' => $rows,
]);
}
查看数据库架构:https://github.com/AntoninSlejska/yii-test/blob/master/example/sql/example-schema.png
在视图 view.php 中显示来自 tables_2-4 的数据,这些数据与 table_1:
相关
foreach($rows as $row) {
echo $row['t2_field_1'];
echo $row['t2_field_2'];
...
}
参见:
和:http://www.yiiframework.com/doc-2.0/yii-db-query.html
它有效,但我不确定它是否是最正确的 Yii2 方式。
我试图在模型 TableOne 中定义关系:
public function getTableTwoRecords()
{
return $this->hasMany(TableTwo::className(), ['t1_id' => 'id']);
}
public function getTableThreeRecords()
{
return $this->hasMany(TableThree::className(), ['id' => 't3_id'])
->via('tableTwoRecords');
}
public function getTableFourRecords()
{
return $this->hasMany(TableFour::className(), ['id' => 't4_id'])
->via('tableThreeRecords');
}
然后在控制器TableOneController中加入记录:
$records = TableOne::find()
->innerJoinWith(['tableTwoRecords'])
->innerJoinWith(['tableThreeRecords'])
->innerJoinWith(['tableFourRecords'])
->all();
但它不起作用。如果我只加入前三个 table,那么它就可以工作。如果我添加第四个 table,则会收到以下错误消息:"Getting unknown property: frontend\models\TableOne::t3_id"
如果我以这种方式更改函数 getTableFourRecords():
public function getTableFourRecords()
{
return $this->hasOne(TableThree::className(), ['t4_id' => 'id']);
}
然后我收到此错误消息:“SQLSTATE[42S22]:未找到列:1054 'on clause' 中的未知列 'table_4.t4_id'
正在执行的 SQL 是:SELECT table_1
.* FROM table_1
INNER JOIN table_2
ON table_1
.id
= table_2
.t1_id
内部联接 table_3
开启 table_2
.t3_id
= table_3
.id
内部联接 table_4
开启 table_1
.id
= table_4
.t4_id
"
您应该必须在关系中定义键值对,例如:
class Customer extends ActiveRecord
{
public function getOrders()
{
return $this->hasMany(Order::className(), ['customer_id' => 'id']); // Always KEY => VALUE pair this relation relate to hasMany relation
}
}
class Order extends ActiveRecord
{
public function getCustomer()
{
return $this->hasOne(Customer::className(), ['id' => 'customer_id']);
// Always KEY => VALUE pair this relation relate to hasOne relation
}
}
现在在你的第四个关系中使用:
public function getTableFourRecords()
{
return $this->hasOne(TableThree::className(), ['id' => 't4_id']);
}
您可以在 ActiveRecord 上阅读更多内容 here
基于 softark 的answer,最简单的解决方案可能如下所示:
模型表一:
public function getTableTwoRecords()
{
return $this->hasMany(TableTwo::className(), ['t1_id' => 'id']);
}
模型表二:
public function getTableThreeRecord()
{
return $this->hasOne(TableThree::className(), ['id' => 't3_id']);
}
模型表三:
public function getTableFourRecord()
{
return $this->hasOne(TableFour::className(), ['id' => 't4_id']);
}
控制器 TableOneController:
public function actionView($id)
{
return $this->render('view', [
'model' => $this->findModel($id),
]);
}
观点table-one/view.php:
foreach ($model->tableTwoRecords as $record) {
echo ' Table 2 >> ';
echo ' ID: ' . $record->id;
echo ' T1 ID: ' . $record->t1_id;
echo ' T3 ID: ' . $record->t3_id;
echo ' Table 3 >> ';
echo ' ID: ' . $record->tableThreeRecord->id;
echo ' T4 ID: ' . $record->tableThreeRecord->t4_id;
echo ' Table 4 >> ';
echo ' ID: ' . $record->tableThreeRecord->tableFourRecord->id;
echo ' <br>';
}
基于 GridView 的 解决方案也是可能的。
模型表二:
public function getTableOneRecord()
{
return $this->hasOne(TableOne::className(), ['id' => 't1_id']);
}
public function getTableThreeRecord()
{
return $this->hasOne(TableThree::className(), ['id' => 't3_id']);
}
public function getTableFourRecord()
{
return $this->hasOne(TableFour::className(), ['id' => 't4_id'])
->via('tableThreeRecord');
}
TableOneController 中使用 Gii 为模型 TableTwo 生成的函数 actionView 已编辑:
use app\models\TableTwo;
use app\models\TableTwoSearch;
...
public function actionView($id)
{
$searchModel = new TableTwoSearch([
't1_id' => $id, // the data have to be filtered by the id of the displayed record
]);
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('view', [
'model' => $this->findModel($id),
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
还有浏览量/table-one/view.php:
echo GridView::widget([
'dataProvider' => $dataProvider,
'columns' => [
'id',
't1_id',
'tableOneRecord.id',
't3_id',
'tableThreeRecord.id',
'tableThreeRecord.t4_id',
'tableFourRecord.id',
],
]);
在控制器中我有以下代码:
public function actionView($id)
{
$query = new Query;
$query->select('*')
->from('table_1 t1')
->innerJoin('table_2 t2', 't2.t1_id = t1.id')
->innerJoin('table_3 t3', 't2.t3_id = t3.id')
->innerJoin('table_4 t4', 't3.t4_id = t4.id')
->andWhere('t1.id = ' . $id);
$rows = $query->all();
return $this->render('view', [
'model' => $this->findModel($id),
'rows' => $rows,
]);
}
查看数据库架构:https://github.com/AntoninSlejska/yii-test/blob/master/example/sql/example-schema.png
在视图 view.php 中显示来自 tables_2-4 的数据,这些数据与 table_1:
相关foreach($rows as $row) {
echo $row['t2_field_1'];
echo $row['t2_field_2'];
...
}
参见:
它有效,但我不确定它是否是最正确的 Yii2 方式。
我试图在模型 TableOne 中定义关系:
public function getTableTwoRecords()
{
return $this->hasMany(TableTwo::className(), ['t1_id' => 'id']);
}
public function getTableThreeRecords()
{
return $this->hasMany(TableThree::className(), ['id' => 't3_id'])
->via('tableTwoRecords');
}
public function getTableFourRecords()
{
return $this->hasMany(TableFour::className(), ['id' => 't4_id'])
->via('tableThreeRecords');
}
然后在控制器TableOneController中加入记录:
$records = TableOne::find()
->innerJoinWith(['tableTwoRecords'])
->innerJoinWith(['tableThreeRecords'])
->innerJoinWith(['tableFourRecords'])
->all();
但它不起作用。如果我只加入前三个 table,那么它就可以工作。如果我添加第四个 table,则会收到以下错误消息:"Getting unknown property: frontend\models\TableOne::t3_id"
如果我以这种方式更改函数 getTableFourRecords():
public function getTableFourRecords()
{
return $this->hasOne(TableThree::className(), ['t4_id' => 'id']);
}
然后我收到此错误消息:“SQLSTATE[42S22]:未找到列:1054 'on clause' 中的未知列 'table_4.t4_id'
正在执行的 SQL 是:SELECT table_1
.* FROM table_1
INNER JOIN table_2
ON table_1
.id
= table_2
.t1_id
内部联接 table_3
开启 table_2
.t3_id
= table_3
.id
内部联接 table_4
开启 table_1
.id
= table_4
.t4_id
"
您应该必须在关系中定义键值对,例如:
class Customer extends ActiveRecord
{
public function getOrders()
{
return $this->hasMany(Order::className(), ['customer_id' => 'id']); // Always KEY => VALUE pair this relation relate to hasMany relation
}
}
class Order extends ActiveRecord
{
public function getCustomer()
{
return $this->hasOne(Customer::className(), ['id' => 'customer_id']);
// Always KEY => VALUE pair this relation relate to hasOne relation
}
}
现在在你的第四个关系中使用:
public function getTableFourRecords()
{
return $this->hasOne(TableThree::className(), ['id' => 't4_id']);
}
您可以在 ActiveRecord 上阅读更多内容 here
基于 softark 的answer,最简单的解决方案可能如下所示:
模型表一:
public function getTableTwoRecords()
{
return $this->hasMany(TableTwo::className(), ['t1_id' => 'id']);
}
模型表二:
public function getTableThreeRecord()
{
return $this->hasOne(TableThree::className(), ['id' => 't3_id']);
}
模型表三:
public function getTableFourRecord()
{
return $this->hasOne(TableFour::className(), ['id' => 't4_id']);
}
控制器 TableOneController:
public function actionView($id)
{
return $this->render('view', [
'model' => $this->findModel($id),
]);
}
观点table-one/view.php:
foreach ($model->tableTwoRecords as $record) {
echo ' Table 2 >> ';
echo ' ID: ' . $record->id;
echo ' T1 ID: ' . $record->t1_id;
echo ' T3 ID: ' . $record->t3_id;
echo ' Table 3 >> ';
echo ' ID: ' . $record->tableThreeRecord->id;
echo ' T4 ID: ' . $record->tableThreeRecord->t4_id;
echo ' Table 4 >> ';
echo ' ID: ' . $record->tableThreeRecord->tableFourRecord->id;
echo ' <br>';
}
基于 GridView 的 解决方案也是可能的。
模型表二:
public function getTableOneRecord()
{
return $this->hasOne(TableOne::className(), ['id' => 't1_id']);
}
public function getTableThreeRecord()
{
return $this->hasOne(TableThree::className(), ['id' => 't3_id']);
}
public function getTableFourRecord()
{
return $this->hasOne(TableFour::className(), ['id' => 't4_id'])
->via('tableThreeRecord');
}
TableOneController 中使用 Gii 为模型 TableTwo 生成的函数 actionView 已编辑:
use app\models\TableTwo;
use app\models\TableTwoSearch;
...
public function actionView($id)
{
$searchModel = new TableTwoSearch([
't1_id' => $id, // the data have to be filtered by the id of the displayed record
]);
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('view', [
'model' => $this->findModel($id),
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
还有浏览量/table-one/view.php:
echo GridView::widget([
'dataProvider' => $dataProvider,
'columns' => [
'id',
't1_id',
'tableOneRecord.id',
't3_id',
'tableThreeRecord.id',
'tableThreeRecord.t4_id',
'tableFourRecord.id',
],
]);