Browse Source

initial draft of saving related models.

tags/2.0.0-beta
Qiang Xue 12 years ago
parent
commit
ef8b092278
  1. 10
      framework/db/ActiveQuery.php
  2. 186
      framework/db/ActiveRecord.php
  3. 14
      framework/db/ActiveRelation.php

10
framework/db/ActiveQuery.php

@ -253,18 +253,10 @@ class ActiveQuery extends Query
$t = strtolower($name); $t = strtolower($name);
if (!isset($relations[$t])) { if (!isset($relations[$t])) {
$getter = 'get' . $name; $relation = $model->getRelation($name);
if (!method_exists($model, $getter)) {
throw new Exception("Unknown relation: $name");
}
$relation = $model->$getter();
if ($relation instanceof ActiveRelation) {
$relation->primaryModel = null; $relation->primaryModel = null;
$relations[$t] = $relation; $relations[$t] = $relation;
} else { } else {
throw new Exception("Unknown relation: $name");
}
} else {
$relation = $relations[$t]; $relation = $relations[$t];
} }

186
framework/db/ActiveRecord.php

@ -13,6 +13,7 @@ namespace yii\db;
use yii\base\Model; use yii\base\Model;
use yii\base\Event; use yii\base\Event;
use yii\base\ModelEvent; use yii\base\ModelEvent;
use yii\base\BadMethodException;
use yii\db\Exception; use yii\db\Exception;
use yii\db\Connection; use yii\db\Connection;
use yii\db\TableSchema; use yii\db\TableSchema;
@ -768,6 +769,7 @@ abstract class ActiveRecord extends Model
* Returns the primary key value. * Returns the primary key value.
* @param boolean $asArray whether to return the primary key value as an array. If true, * @param boolean $asArray whether to return the primary key value as an array. If true,
* the return value will be an array with column name as key and column value as value. * the return value will be an array with column name as key and column value as value.
* Note that for composite primary keys, an array will always be returned regardless of this parameter value.
* @return mixed the primary key value. An array (column name=>column value) is returned if the primary key is composite. * @return mixed the primary key value. An array (column name=>column value) is returned if the primary key is composite.
* If primary key is not defined, null will be returned. * If primary key is not defined, null will be returned.
*/ */
@ -859,27 +861,185 @@ abstract class ActiveRecord extends Model
/** /**
* @param string $name * @param string $name
* @param ActiveRecord $model * @param ActiveRecord $model
* @throws Exception
*/ */
public function linkWith($name, $model) public function link($name, $model, $extraAttributes = array())
{ {
$getter = 'get' . $name; $relation = $this->getRelation($name);
if (!method_exists($this, $getter)) {
throw new Exception('Unknown relation: ' . $name); if ($relation->via !== null) {
if (is_array($relation->via)) {
/** @var $viaQuery ActiveRelation */
list($viaName, $viaQuery) = $relation->via[1];
/** @var $viaClass ActiveRecord */
$viaClass = $viaQuery->modelClass;
$viaTable = $viaClass::tableName();
} else {
$viaQuery = $relation->via;
$viaTable = reset($relation->via->from);
} }
$relation = $this->$getter(); $columns = array();
if (!$relation instanceof ActiveRelation) { foreach ($viaQuery->link as $a => $b) {
throw new Exception('Unknown relation: ' . $name); $columns[$a] = $this->$b;
}
foreach ($relation->link as $a => $b) {
$columns[$b] = $model->$a;
}
foreach ($extraAttributes as $k => $v) {
$columns[$k] = $v;
}
$command = $this->getDbConnection()->createCommand();
$command->insert($viaTable, $columns)->execute();
return;
// todo: update $viaName
}
$keys = $model->primaryKey();
$p1 = true;
foreach (array_keys($relation->link) as $key) {
if (!in_array($key, $keys, true)) {
$p1 = false;
break;
}
}
$keys = $this->primaryKey();
$p2 = true;
foreach (array_values($relation->link) as $key) {
if (!in_array($key, $keys, true)) {
$p2 = false;
break;
}
}
if ($p1 && $p2) {
if ($this->getIsNewRecord() && $model->getIsNewRecord()) {
throw new Exception('both new');
} elseif ($this->getIsNewRecord()) {
foreach ($relation->link as $a => $b) {
$value = $model->$a;
if ($value === null) {
throw new Exception('key null');
}
$this->$b = $value;
}
$this->save(false);
} elseif ($model->getIsNewRecord()) {
foreach ($relation->link as $a => $b) {
$value = $this->$b;
if ($value === null) {
throw new Exception('key null');
}
$model->$a = $value;
} }
if ($relation->multiple) { $model->save(false);
} else {
throw new Exception('both old');
}
} elseif ($p1) {
foreach ($relation->link as $a => $b) { foreach ($relation->link as $a => $b) {
$key = $this->$b; $value = $model->$a;
if ($key === null) { if ($value === null) {
throw new Exception('key null'); throw new Exception('key null');
} }
$model->$a = $this->$b; $this->$b = $value;
}
$this->save(false);
} elseif ($p2) {
foreach ($relation->link as $a => $b) {
$value = $this->$b;
if ($value === null) {
throw new Exception('key null');
}
$model->$a = $value;
}
$model->save(false);
} else {
throw new Exception('');
}
// todo: update relation models
}
/**
* @param string $name
* @param ActiveRecord $model
* @throws Exception
*/
public function unlink($name, $model)
{
$relation = $this->getRelation($name);
if ($relation->via !== null) {
if (is_array($relation->via)) {
/** @var $viaQuery ActiveRelation */
$viaQuery = $relation->via[1];
/** @var $viaClass ActiveRecord */
$viaClass = $viaQuery->modelClass;
$viaTable = $viaClass::tableName();
} else {
$viaQuery = $relation->via;
$viaTable = reset($relation->via->from);
}
$columns = array();
foreach ($viaQuery->link as $a => $b) {
$columns[$a] = $this->$b;
}
foreach ($relation->link as $a => $b) {
$columns[$b] = $model->$a;
}
$command = $this->getDbConnection()->createCommand();
$command->delete($viaTable, $columns)->execute();
return;
}
$keys = $model->primaryKey();
$p1 = true;
foreach (array_keys($relation->link) as $key) {
if (!in_array($key, $keys, true)) {
$p1 = false;
break;
}
}
$keys = $this->primaryKey();
$p2 = true;
foreach (array_values($relation->link) as $key) {
if (!in_array($key, $keys, true)) {
$p2 = false;
break;
}
} }
return $model->save(false); if ($p1 && $p2) {
foreach ($relation->link as $a => $b) {
$model->$a = null;
}
$model->save(false);
} elseif ($p1) {
foreach ($relation->link as $b) {
$this->$b = null;
}
$this->save(false);
} elseif ($p2) {
foreach ($relation->link as $a => $b) {
$model->$a = null;
}
$model->save(false);
} else {
throw new Exception('');
}
// todo: update relation models
} }
/**
* @param string $name
* @return ActiveRelation
* @throws Exception
*/
public function getRelation($name)
{
$getter = 'get' . $name;
try {
$relation = $this->$getter();
if ($relation instanceof ActiveRelation) {
return $relation;
}
} catch (BadMethodException $e) {
}
throw new Exception('Unknown relation: ' . $name);
} }
} }

14
framework/db/ActiveRelation.php

@ -12,7 +12,6 @@ namespace yii\db;
use yii\db\Connection; use yii\db\Connection;
use yii\db\Command; use yii\db\Command;
use yii\db\QueryBuilder;
/** /**
* It is used in three scenarios: * It is used in three scenarios:
@ -45,7 +44,7 @@ class ActiveRelation extends ActiveQuery
/** /**
* @var array|ActiveRelation * @var array|ActiveRelation
*/ */
protected $via; public $via;
/** /**
* @param string $relationName * @param string $relationName
@ -55,20 +54,13 @@ class ActiveRelation extends ActiveQuery
*/ */
public function via($relationName, $callback = null) public function via($relationName, $callback = null)
{ {
$getter = 'get' . $relationName; $relation = $this->primaryModel->getRelation($relationName);
if (method_exists($this->primaryModel, $getter)) {
$relation = $this->primaryModel->$getter();
if ($relation instanceof ActiveRelation) {
$relation->primaryModel = null;
$this->via = array($relationName, $relation); $this->via = array($relationName, $relation);
if ($callback !== null) { if ($callback !== null) {
call_user_func($callback, $relation); call_user_func($callback, $relation);
} }
return $this; return $this;
} }
}
throw new Exception('Unknown relation: ' . $relationName);
}
/** /**
* @param string $tableName * @param string $tableName
@ -110,7 +102,6 @@ class ActiveRelation extends ActiveQuery
// via relation // via relation
/** @var $viaQuery ActiveRelation */ /** @var $viaQuery ActiveRelation */
list($viaName, $viaQuery) = $this->via; list($viaName, $viaQuery) = $this->via;
$viaQuery->primaryModel = $this->primaryModel;
if ($viaQuery->multiple) { if ($viaQuery->multiple) {
$viaModels = $viaQuery->all(); $viaModels = $viaQuery->all();
$this->primaryModel->populateRelation($viaName, $viaModels); $this->primaryModel->populateRelation($viaName, $viaModels);
@ -143,6 +134,7 @@ class ActiveRelation extends ActiveQuery
// via relation // via relation
/** @var $viaQuery ActiveRelation */ /** @var $viaQuery ActiveRelation */
list($viaName, $viaQuery) = $this->via; list($viaName, $viaQuery) = $this->via;
$viaQuery->primaryModel = null;
$viaModels = $viaQuery->findWith($viaName, $primaryModels); $viaModels = $viaQuery->findWith($viaName, $primaryModels);
$this->filterByModels($viaModels); $this->filterByModels($viaModels);
} else { } else {

Loading…
Cancel
Save