Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
Y
yii2
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
PSDI Army
yii2
Commits
46ab456f
Commit
46ab456f
authored
Jan 12, 2013
by
Qiang Xue
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
finished new AR.
parent
cf6c73c6
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
275 additions
and
236 deletions
+275
-236
ActiveRecord.php
framework/db/ActiveRecord.php
+180
-234
Customer.php
tests/unit/data/ar/Customer.php
+1
-1
Order.php
tests/unit/data/ar/Order.php
+11
-0
mysql.sql
tests/unit/data/mysql.sql
+2
-1
ActiveRecordTest.php
tests/unit/framework/db/ActiveRecordTest.php
+81
-0
No files found.
framework/db/ActiveRecord.php
View file @
46ab456f
...
@@ -191,10 +191,12 @@ abstract class ActiveRecord extends Model
...
@@ -191,10 +191,12 @@ abstract class ActiveRecord extends Model
public
static
function
updateAllCounters
(
$counters
,
$condition
=
''
,
$params
=
array
())
public
static
function
updateAllCounters
(
$counters
,
$condition
=
''
,
$params
=
array
())
{
{
$db
=
static
::
getDbConnection
();
$db
=
static
::
getDbConnection
();
$n
=
0
;
foreach
(
$counters
as
$name
=>
$value
)
{
foreach
(
$counters
as
$name
=>
$value
)
{
$value
=
(
int
)
$value
;
$quotedName
=
$db
->
quoteColumnName
(
$name
);
$quotedName
=
$db
->
quoteColumnName
(
$name
,
true
);
$counters
[
$name
]
=
new
Expression
(
"
$quotedName
+:cv
{
$n
}
"
);
$counters
[
$name
]
=
new
Expression
(
$value
>=
0
?
"
$quotedName
+
$value
"
:
"
$quotedName$value
"
);
$params
[
":cv
{
$n
}
"
]
=
$value
;
$n
++
;
}
}
$command
=
$db
->
createCommand
();
$command
=
$db
->
createCommand
();
$command
->
update
(
static
::
tableName
(),
$counters
,
$condition
,
$params
);
$command
->
update
(
static
::
tableName
(),
$counters
,
$condition
,
$params
);
...
@@ -222,19 +224,21 @@ abstract class ActiveRecord extends Model
...
@@ -222,19 +224,21 @@ abstract class ActiveRecord extends Model
*/
*/
public
static
function
createQuery
()
public
static
function
createQuery
()
{
{
return
new
ActiveQuery
(
array
(
'modelClass'
=>
get_called_class
()));
return
new
ActiveQuery
(
array
(
'modelClass'
=>
get_called_class
(),
));
}
}
/**
/**
* Declares the name of the database table associated with this AR class.
* Declares the name of the database table associated with this AR class.
* By default this method returns the class name as the table name by calling [[StringHelper::camel2id()]]
.
* By default this method returns the class name as the table name by calling [[StringHelper::camel2id()]]
*
For example, 'Customer' becomes 'customer', and 'OrderDetail' becomes 'order_detail'.
*
with prefix 'tbl_'. For example, 'Customer' becomes 'tbl_customer', and 'OrderDetail' becomes
* You may override this method if the table is not named after this convention.
*
'tbl_order_detail'.
You may override this method if the table is not named after this convention.
* @return string the table name
* @return string the table name
*/
*/
public
static
function
tableName
()
public
static
function
tableName
()
{
{
return
StringHelper
::
camel2id
(
basename
(
get_called_class
()),
'_'
);
return
'tbl_'
.
StringHelper
::
camel2id
(
basename
(
get_called_class
()),
'_'
);
}
}
/**
/**
...
@@ -261,18 +265,6 @@ abstract class ActiveRecord extends Model
...
@@ -261,18 +265,6 @@ abstract class ActiveRecord extends Model
}
}
/**
/**
* Returns the default named scope that should be implicitly applied to all queries for this model.
* Note, the default scope only applies to SELECT queries. It is ignored for INSERT, UPDATE and DELETE queries.
* The default implementation simply returns an empty array. You may override this method
* if the model needs to be queried with some default criteria (e.g. only non-deleted users should be returned).
* @param ActiveQuery
*/
public
static
function
defaultScope
(
$query
)
{
// todo: should we drop this?
}
/**
* PHP getter magic method.
* PHP getter magic method.
* This method is overridden so that attributes and related objects can be accessed like properties.
* This method is overridden so that attributes and related objects can be accessed like properties.
* @param string $name property name
* @param string $name property name
...
@@ -422,11 +414,8 @@ abstract class ActiveRecord extends Model
...
@@ -422,11 +414,8 @@ abstract class ActiveRecord extends Model
/**
/**
* Returns the named attribute value.
* Returns the named attribute value.
* If this is a new record and the attribute is not set before,
* the default column value will be returned.
* If this record is the result of a query and the attribute is not loaded,
* If this record is the result of a query and the attribute is not loaded,
* null will be returned.
* null will be returned.
* You may also use $this->AttributeName to obtain the attribute value.
* @param string $name the attribute name
* @param string $name the attribute name
* @return mixed the attribute value. Null if the attribute is not set or does not exist.
* @return mixed the attribute value. Null if the attribute is not set or does not exist.
* @see hasAttribute
* @see hasAttribute
...
@@ -438,7 +427,6 @@ abstract class ActiveRecord extends Model
...
@@ -438,7 +427,6 @@ abstract class ActiveRecord extends Model
/**
/**
* Sets the named attribute value.
* Sets the named attribute value.
* You may also use $this->AttributeName to set the attribute value.
* @param string $name the attribute name
* @param string $name the attribute name
* @param mixed $value the attribute value.
* @param mixed $value the attribute value.
* @see hasAttribute
* @see hasAttribute
...
@@ -449,6 +437,45 @@ abstract class ActiveRecord extends Model
...
@@ -449,6 +437,45 @@ abstract class ActiveRecord extends Model
}
}
/**
/**
* Returns the old value of the named attribute.
* If this record is the result of a query and the attribute is not loaded,
* null will be returned.
* @param string $name the attribute name
* @return mixed the old attribute value. Null if the attribute is not loaded before
* or does not exist.
* @see hasAttribute
*/
public
function
getOldAttribute
(
$name
)
{
return
isset
(
$this
->
_oldAttributes
[
$name
])
?
$this
->
_oldAttributes
[
$name
]
:
null
;
}
/**
* Sets the old value of the named attribute.
* @param string $name the attribute name
* @param mixed $value the old attribute value.
* @see hasAttribute
*/
public
function
setOldAttribute
(
$name
,
$value
)
{
$this
->
_oldAttributes
[
$name
]
=
$value
;
}
/**
* Returns a value indicating whether the named attribute has been changed.
* @param string $name the name of the attribute
* @return boolean whether the attribute has been changed
*/
public
function
isAttributeChanged
(
$name
)
{
if
(
isset
(
$this
->
_attribute
[
$name
],
$this
->
_oldAttributes
[
$name
]))
{
return
$this
->
_attribute
[
$name
]
!==
$this
->
_oldAttributes
[
$name
];
}
else
{
return
isset
(
$this
->
_attributes
[
$name
])
||
isset
(
$this
->
_oldAttributes
);
}
}
/**
* Returns the attribute values that have been modified since they are loaded or saved most recently.
* Returns the attribute values that have been modified since they are loaded or saved most recently.
* @param string[]|null $names the names of the attributes whose values may be returned if they are
* @param string[]|null $names the names of the attributes whose values may be returned if they are
* changed recently. If null, [[attributes()]] will be used.
* changed recently. If null, [[attributes()]] will be used.
...
@@ -480,19 +507,18 @@ abstract class ActiveRecord extends Model
...
@@ -480,19 +507,18 @@ abstract class ActiveRecord extends Model
/**
/**
* Saves the current record.
* Saves the current record.
*
*
* The record is inserted as a row into the database table if its
{@link isNewRecord}
* The record is inserted as a row into the database table if its
[[isNewRecord]]
* property is true (usually the case when the record is created using the 'new'
* property is true (usually the case when the record is created using the 'new'
* operator). Otherwise, it will be used to update the corresponding row in the table
* operator). Otherwise, it will be used to update the corresponding row in the table
* (usually the case if the record is obtained using one of those 'find' methods.)
* (usually the case if the record is obtained using one of those 'find' methods.)
*
*
* Validation will be performed before saving the record. If the validation fails,
* Validation will be performed before saving the record. If the validation fails,
* the record will not be saved. You can call
{@link getErrors()}
to retrieve the
* the record will not be saved. You can call
[[getErrors()]]
to retrieve the
* validation errors.
* validation errors.
*
*
* If the record is saved via insertion, its {@link isNewRecord} property will be
* If the record is saved via insertion, and if its primary key is auto-incremental
* set false, and its {@link scenario} property will be set to be 'update'.
* and is not set before insertion, the primary key will be populated with the
* And if its primary key is auto-incremental and is not set before insertion,
* automatically generated key value.
* the primary key will be populated with the automatically generated key value.
*
*
* @param boolean $runValidation whether to perform validation before saving the record.
* @param boolean $runValidation whether to perform validation before saving the record.
* If the validation fails, the record will not be saved to database.
* If the validation fails, the record will not be saved to database.
...
@@ -512,9 +538,7 @@ abstract class ActiveRecord extends Model
...
@@ -512,9 +538,7 @@ abstract class ActiveRecord extends Model
* Inserts a row into the table based on this active record attributes.
* Inserts a row into the table based on this active record attributes.
* If the table's primary key is auto-incremental and is null before insertion,
* If the table's primary key is auto-incremental and is null before insertion,
* it will be populated with the actual value after insertion.
* it will be populated with the actual value after insertion.
* Note, validation is not performed in this method. You may call {@link validate} to perform the validation.
* Note, validation is not performed in this method. You may call [[validate()]] to perform the validation.
* After the record is inserted to DB successfully, its {@link isNewRecord} property will be set false,
* and its {@link scenario} property will be set to be 'update'.
* @param array $attributes list of attributes that need to be saved. Defaults to null,
* @param array $attributes list of attributes that need to be saved. Defaults to null,
* meaning all attributes that are loaded from DB will be saved.
* meaning all attributes that are loaded from DB will be saved.
* @return boolean whether the attributes are valid and the record is inserted successfully.
* @return boolean whether the attributes are valid and the record is inserted successfully.
...
@@ -522,8 +546,13 @@ abstract class ActiveRecord extends Model
...
@@ -522,8 +546,13 @@ abstract class ActiveRecord extends Model
*/
*/
public
function
insert
(
$attributes
=
null
)
public
function
insert
(
$attributes
=
null
)
{
{
if
(
$this
->
before
Insert
(
))
{
if
(
$this
->
before
Save
(
true
))
{
$values
=
$this
->
getChangedAttributes
(
$attributes
);
$values
=
$this
->
getChangedAttributes
(
$attributes
);
if
(
$values
===
array
())
{
foreach
(
$this
->
primaryKey
()
as
$key
)
{
$values
[
$key
]
=
isset
(
$this
->
_attributes
[
$key
])
?
$this
->
_attributes
[
$key
]
:
null
;
}
}
$db
=
$this
->
getDbConnection
();
$db
=
$this
->
getDbConnection
();
$command
=
$db
->
createCommand
()
->
insert
(
$this
->
tableName
(),
$values
);
$command
=
$db
->
createCommand
()
->
insert
(
$this
->
tableName
(),
$values
);
if
(
$command
->
execute
())
{
if
(
$command
->
execute
())
{
...
@@ -539,7 +568,7 @@ abstract class ActiveRecord extends Model
...
@@ -539,7 +568,7 @@ abstract class ActiveRecord extends Model
foreach
(
$values
as
$name
=>
$value
)
{
foreach
(
$values
as
$name
=>
$value
)
{
$this
->
_oldAttributes
[
$name
]
=
$value
;
$this
->
_oldAttributes
[
$name
]
=
$value
;
}
}
$this
->
after
Insert
(
);
$this
->
after
Save
(
true
);
return
true
;
return
true
;
}
}
}
}
...
@@ -549,25 +578,21 @@ abstract class ActiveRecord extends Model
...
@@ -549,25 +578,21 @@ abstract class ActiveRecord extends Model
/**
/**
* Updates the row represented by this active record.
* Updates the row represented by this active record.
* All loaded attributes will be saved to the database.
* All loaded attributes will be saved to the database.
* Note, validation is not performed in this method. You may call
{@link validate}
to perform the validation.
* Note, validation is not performed in this method. You may call
[[validate()]]
to perform the validation.
* @param array $attributes list of attributes that need to be saved. Defaults to null,
* @param array $attributes list of attributes that need to be saved. Defaults to null,
* meaning all attributes that are loaded from DB will be saved.
* meaning all attributes that are loaded from DB will be saved.
* @return boolean whether the update is successful
* @return boolean whether the update is successful
* @throws Exception if the record is new
*/
*/
public
function
update
(
$attributes
=
null
)
public
function
update
(
$attributes
=
null
)
{
{
if
(
$this
->
getIsNewRecord
())
{
if
(
$this
->
beforeSave
(
false
))
{
throw
new
Exception
(
'The active record cannot be updated because it is new.'
);
}
if
(
$this
->
beforeUpdate
())
{
$values
=
$this
->
getChangedAttributes
(
$attributes
);
$values
=
$this
->
getChangedAttributes
(
$attributes
);
if
(
$values
!==
array
())
{
if
(
$values
!==
array
())
{
$this
->
updateAll
(
$values
,
$this
->
getOldPrimaryKey
(
true
));
$this
->
updateAll
(
$values
,
$this
->
getOldPrimaryKey
(
true
));
foreach
(
$values
as
$name
=>
$value
)
{
foreach
(
$values
as
$name
=>
$value
)
{
$this
->
_oldAttributes
[
$name
]
=
$this
->
_attributes
[
$name
];
$this
->
_oldAttributes
[
$name
]
=
$this
->
_attributes
[
$name
];
}
}
$this
->
after
Update
(
);
$this
->
after
Save
(
false
);
}
}
return
true
;
return
true
;
}
else
{
}
else
{
...
@@ -609,13 +634,9 @@ abstract class ActiveRecord extends Model
...
@@ -609,13 +634,9 @@ abstract class ActiveRecord extends Model
/**
/**
* Deletes the row corresponding to this active record.
* Deletes the row corresponding to this active record.
* @return boolean whether the deletion is successful.
* @return boolean whether the deletion is successful.
* @throws Exception if the record is new or any database error
*/
*/
public
function
delete
()
public
function
delete
()
{
{
if
(
$this
->
getIsNewRecord
())
{
throw
new
Exception
(
'The active record cannot be deleted because it is new.'
);
}
if
(
$this
->
beforeDelete
())
{
if
(
$this
->
beforeDelete
())
{
$result
=
$this
->
deleteAll
(
$this
->
getPrimaryKey
(
true
))
>
0
;
$result
=
$this
->
deleteAll
(
$this
->
getPrimaryKey
(
true
))
>
0
;
$this
->
_oldAttributes
=
null
;
$this
->
_oldAttributes
=
null
;
...
@@ -628,10 +649,7 @@ abstract class ActiveRecord extends Model
...
@@ -628,10 +649,7 @@ abstract class ActiveRecord extends Model
/**
/**
* Returns if the current record is new.
* Returns if the current record is new.
* @return boolean whether the record is new and should be inserted when calling {@link save}.
* @return boolean whether the record is new and should be inserted when calling [[save()]].
* This property is automatically set in constructor and {@link populateRecord}.
* Defaults to false, but it will be set to true if the instance is created using
* the new operator.
*/
*/
public
function
getIsNewRecord
()
public
function
getIsNewRecord
()
{
{
...
@@ -640,7 +658,7 @@ abstract class ActiveRecord extends Model
...
@@ -640,7 +658,7 @@ abstract class ActiveRecord extends Model
/**
/**
* Sets if the record is new.
* Sets if the record is new.
* @param boolean $value whether the record is new and should be inserted when calling
{@link save}
.
* @param boolean $value whether the record is new and should be inserted when calling
[[save()]]
.
* @see getIsNewRecord
* @see getIsNewRecord
*/
*/
public
function
setIsNewRecord
(
$value
)
public
function
setIsNewRecord
(
$value
)
...
@@ -648,58 +666,16 @@ abstract class ActiveRecord extends Model
...
@@ -648,58 +666,16 @@ abstract class ActiveRecord extends Model
$this
->
_oldAttributes
=
$value
?
null
:
$this
->
_attributes
;
$this
->
_oldAttributes
=
$value
?
null
:
$this
->
_attributes
;
}
}
/**
public
function
beforeSave
(
$insert
)
* This method is invoked before saving a record (after validation, if any).
* The default implementation raises the `beforeSave` event.
* You may override this method to do any preparation work for record saving.
* Use {@link isNewRecord} to determine whether the saving is
* for inserting or updating record.
* Make sure you call the parent implementation so that the event is raised properly.
* @return boolean whether the saving should be executed. Defaults to true.
*/
public
function
beforeInsert
()
{
$event
=
new
ModelEvent
(
$this
);
$this
->
trigger
(
'beforeInsert'
,
$event
);
return
$event
->
isValid
;
}
/**
* This method is invoked after saving a record successfully.
* The default implementation raises the `afterSave` event.
* You may override this method to do postprocessing after record saving.
* Make sure you call the parent implementation so that the event is raised properly.
*/
public
function
afterInsert
()
{
$this
->
trigger
(
'afterInsert'
,
new
Event
(
$this
));
}
/**
* This method is invoked before saving a record (after validation, if any).
* The default implementation raises the `beforeSave` event.
* You may override this method to do any preparation work for record saving.
* Use {@link isNewRecord} to determine whether the saving is
* for inserting or updating record.
* Make sure you call the parent implementation so that the event is raised properly.
* @return boolean whether the saving should be executed. Defaults to true.
*/
public
function
beforeUpdate
()
{
{
$event
=
new
ModelEvent
(
$this
);
$event
=
new
ModelEvent
(
$this
);
$this
->
trigger
(
'beforeUpdate'
,
$event
);
$this
->
trigger
(
$insert
?
'beforeInsert'
:
'beforeUpdate'
,
$event
);
return
$event
->
isValid
;
return
$event
->
isValid
;
}
}
/**
public
function
afterSave
(
$insert
)
* This method is invoked after saving a record successfully.
* The default implementation raises the `afterSave` event.
* You may override this method to do postprocessing after record saving.
* Make sure you call the parent implementation so that the event is raised properly.
*/
public
function
afterUpdate
()
{
{
$this
->
trigger
(
'afterUpdate'
,
new
Event
(
$this
)
);
$this
->
trigger
(
$insert
?
'afterInsert'
:
'afterUpdate'
);
}
}
/**
/**
...
@@ -724,7 +700,7 @@ abstract class ActiveRecord extends Model
...
@@ -724,7 +700,7 @@ abstract class ActiveRecord extends Model
*/
*/
public
function
afterDelete
()
public
function
afterDelete
()
{
{
$this
->
trigger
(
'afterDelete'
,
new
Event
(
$this
)
);
$this
->
trigger
(
'afterDelete'
);
}
}
/**
/**
...
@@ -734,10 +710,7 @@ abstract class ActiveRecord extends Model
...
@@ -734,10 +710,7 @@ abstract class ActiveRecord extends Model
*/
*/
public
function
refresh
(
$attributes
=
null
)
public
function
refresh
(
$attributes
=
null
)
{
{
if
(
$this
->
getIsNewRecord
())
{
$record
=
$this
->
find
(
$this
->
getPrimaryKey
(
true
));
return
false
;
}
$record
=
$this
->
find
()
->
where
(
$this
->
getPrimaryKey
(
true
))
->
one
();
if
(
$record
===
null
)
{
if
(
$record
===
null
)
{
return
false
;
return
false
;
}
}
...
@@ -814,6 +787,7 @@ abstract class ActiveRecord extends Model
...
@@ -814,6 +787,7 @@ abstract class ActiveRecord extends Model
/**
/**
* Creates an active record with the given attributes.
* Creates an active record with the given attributes.
* Note that this method does not save the record into database.
* @param array $row attribute values (name => value)
* @param array $row attribute values (name => value)
* @return ActiveRecord the newly created active record.
* @return ActiveRecord the newly created active record.
*/
*/
...
@@ -834,7 +808,7 @@ abstract class ActiveRecord extends Model
...
@@ -834,7 +808,7 @@ abstract class ActiveRecord extends Model
/**
/**
* Creates an active record instance.
* Creates an active record instance.
* This method is called by [[create
Record
()]].
* This method is called by [[create()]].
* You may override this method if the instance being created
* You may override this method if the instance being created
* depends the attributes that are to be populated to the record.
* depends the attributes that are to be populated to the record.
* For example, by creating a record based on the value of a column,
* For example, by creating a record based on the value of a column,
...
@@ -860,112 +834,79 @@ abstract class ActiveRecord extends Model
...
@@ -860,112 +834,79 @@ abstract class ActiveRecord extends Model
/**
/**
* @param string $name
* @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
);
}
/**
* @param string $name
* @param ActiveRecord $model
* @param ActiveRecord $model
* @param array $extraColumns
*/
*/
public
function
link
(
$name
,
$model
,
$extra
Attribute
s
=
array
())
public
function
link
(
$name
,
$model
,
$extra
Column
s
=
array
())
{
{
$relation
=
$this
->
getRelation
(
$name
);
$relation
=
$this
->
getRelation
(
$name
);
if
(
$relation
->
via
!==
null
)
{
if
(
$relation
->
via
!==
null
)
{
if
(
is_array
(
$relation
->
via
))
{
if
(
is_array
(
$relation
->
via
))
{
/** @var $via
Query
ActiveRelation */
/** @var $via
Relation
ActiveRelation */
list
(
$viaName
,
$via
Query
)
=
$relation
->
via
[
1
]
;
list
(
$viaName
,
$via
Relation
)
=
$relation
->
via
;
/** @var $viaClass ActiveRecord */
/** @var $viaClass ActiveRecord */
$viaClass
=
$via
Query
->
modelClass
;
$viaClass
=
$via
Relation
->
modelClass
;
$viaTable
=
$viaClass
::
tableName
();
$viaTable
=
$viaClass
::
tableName
();
// unset $viaName so that it can be reloaded to reflect the change
// unset $viaName so that it can be reloaded to reflect the change
unset
(
$this
->
_related
[
$viaName
]);
unset
(
$this
->
_related
[
strtolower
(
$viaName
)
]);
}
else
{
}
else
{
$via
Query
=
$relation
->
via
;
$via
Relation
=
$relation
->
via
;
$viaTable
=
reset
(
$relation
->
via
->
from
);
$viaTable
=
reset
(
$relation
->
via
->
from
);
}
}
$columns
=
array
();
$columns
=
array
();
foreach
(
$via
Query
->
link
as
$a
=>
$b
)
{
foreach
(
$via
Relation
->
link
as
$a
=>
$b
)
{
$columns
[
$a
]
=
$this
->
$b
;
$columns
[
$a
]
=
$this
->
$b
;
}
}
foreach
(
$relation
->
link
as
$a
=>
$b
)
{
foreach
(
$relation
->
link
as
$a
=>
$b
)
{
$columns
[
$b
]
=
$model
->
$a
;
$columns
[
$b
]
=
$model
->
$a
;
}
}
foreach
(
$extra
Attribute
s
as
$k
=>
$v
)
{
foreach
(
$extra
Column
s
as
$k
=>
$v
)
{
$columns
[
$k
]
=
$v
;
$columns
[
$k
]
=
$v
;
}
}
$command
=
$this
->
getDbConnection
()
->
createCommand
();
$this
->
getDbConnection
()
->
createCommand
()
$command
->
insert
(
$viaTable
,
$columns
)
->
execute
();
->
insert
(
$viaTable
,
$columns
)
->
execute
();
$name
=
strtolower
(
$name
);
}
else
{
if
(
!
$relation
->
multiple
)
{
$p1
=
$model
->
isPrimaryKey
(
array_keys
(
$relation
->
link
));
$this
->
_related
[
$name
]
=
$model
;
$p2
=
$this
->
isPrimaryKey
(
array_values
(
$relation
->
link
));
}
elseif
(
isset
(
$this
->
_related
[
$name
]))
{
if
(
$p1
&&
$p2
)
{
if
(
$relation
->
indexBy
!==
null
)
{
if
(
$this
->
getIsNewRecord
()
&&
$model
->
getIsNewRecord
())
{
$indexBy
=
$relation
->
indexBy
;
throw
new
Exception
(
'both new'
);
$this
->
_related
[
$name
][
$model
->
$indexBy
]
=
$model
;
}
elseif
(
$this
->
getIsNewRecord
())
{
$this
->
bindModels
(
array_flip
(
$relation
->
link
),
$this
,
$model
);
}
elseif
(
$model
->
getIsNewRecord
())
{
$this
->
bindModels
(
$relation
->
link
,
$model
,
$this
);
}
else
{
}
else
{
$this
->
_related
[
$name
][]
=
$model
;
throw
new
Exception
(
'both old'
);
}
}
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
;
}
}
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
(
$p1
)
{
}
elseif
(
$model
->
getIsNewRecord
())
{
$this
->
bindModels
(
array_flip
(
$relation
->
link
),
$this
,
$model
);
foreach
(
$relation
->
link
as
$a
=>
$b
)
{
}
elseif
(
$p2
)
{
$value
=
$this
->
$b
;
$this
->
bindModels
(
$relation
->
link
,
$model
,
$this
);
if
(
$value
===
null
)
{
throw
new
Exception
(
'key null'
);
}
$model
->
$a
=
$value
;
}
$model
->
save
(
false
);
}
else
{
}
else
{
throw
new
Exception
(
'
both old
'
);
throw
new
Exception
(
''
);
}
}
}
elseif
(
$p1
)
{
foreach
(
$relation
->
link
as
$a
=>
$b
)
{
$value
=
$model
->
$a
;
if
(
$value
===
null
)
{
throw
new
Exception
(
'key null'
);
}
$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
(
''
);
}
}
// update lazily loaded related objects
if
(
!
$relation
->
multiple
)
{
if
(
!
$relation
->
multiple
)
{
$this
->
_related
[
$name
]
=
$model
;
$this
->
_related
[
$name
]
=
$model
;
}
elseif
(
isset
(
$this
->
_related
[
$name
]))
{
}
elseif
(
isset
(
$this
->
_related
[
$name
]))
{
...
@@ -976,74 +917,64 @@ abstract class ActiveRecord extends Model
...
@@ -976,74 +917,64 @@ abstract class ActiveRecord extends Model
$this
->
_related
[
$name
][]
=
$model
;
$this
->
_related
[
$name
][]
=
$model
;
}
}
}
}
return
;
}
}
/**
/**
* @param string $name
* @param string $name
* @param ActiveRecord $model
* @param ActiveRecord $model
* @param boolean $delete whether to delete the model that contains the foreign key.
* If false, the model's foreign key will be set null and saved.
* @throws Exception
* @throws Exception
*/
*/
public
function
unlink
(
$name
,
$model
)
public
function
unlink
(
$name
,
$model
,
$delete
=
true
)
{
{
$relation
=
$this
->
getRelation
(
$name
);
$relation
=
$this
->
getRelation
(
$name
);
if
(
$relation
->
via
!==
null
)
{
if
(
$relation
->
via
!==
null
)
{
if
(
is_array
(
$relation
->
via
))
{
if
(
is_array
(
$relation
->
via
))
{
/** @var $via
Query
ActiveRelation */
/** @var $via
Relation
ActiveRelation */
$viaQuery
=
$relation
->
via
[
1
]
;
list
(
$viaName
,
$viaRelation
)
=
$relation
->
via
;
/** @var $viaClass ActiveRecord */
/** @var $viaClass ActiveRecord */
$viaClass
=
$via
Query
->
modelClass
;
$viaClass
=
$via
Relation
->
modelClass
;
$viaTable
=
$viaClass
::
tableName
();
$viaTable
=
$viaClass
::
tableName
();
unset
(
$this
->
_related
[
strtolower
(
$viaName
)]);
}
else
{
}
else
{
$via
Query
=
$relation
->
via
;
$via
Relation
=
$relation
->
via
;
$viaTable
=
reset
(
$relation
->
via
->
from
);
$viaTable
=
reset
(
$relation
->
via
->
from
);
}
}
$columns
=
array
();
$columns
=
array
();
foreach
(
$via
Query
->
link
as
$a
=>
$b
)
{
foreach
(
$via
Relation
->
link
as
$a
=>
$b
)
{
$columns
[
$a
]
=
$this
->
$b
;
$columns
[
$a
]
=
$this
->
$b
;
}
}
foreach
(
$relation
->
link
as
$a
=>
$b
)
{
foreach
(
$relation
->
link
as
$a
=>
$b
)
{
$columns
[
$b
]
=
$model
->
$a
;
$columns
[
$b
]
=
$model
->
$a
;
}
}
$command
=
$this
->
getDbConnection
()
->
createCommand
();
$command
=
$this
->
getDbConnection
()
->
createCommand
();
$command
->
delete
(
$viaTable
,
$columns
)
->
execute
();
if
(
$delete
)
{
return
;
$command
->
delete
(
$viaTable
,
$columns
)
->
execute
();
}
}
else
{
$nulls
=
array
();
$keys
=
$model
->
primaryKey
();
foreach
(
array_keys
(
$columns
)
as
$a
)
{
$p1
=
true
;
$nulls
[
$a
]
=
null
;
foreach
(
array_keys
(
$relation
->
link
)
as
$key
)
{
}
if
(
!
in_array
(
$key
,
$keys
,
true
))
{
$command
->
update
(
$viaTable
,
$nulls
,
$columns
)
->
execute
();
$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
)
{
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
{
}
else
{
throw
new
Exception
(
''
);
$p1
=
$model
->
isPrimaryKey
(
array_keys
(
$relation
->
link
));
$p2
=
$this
->
isPrimaryKey
(
array_values
(
$relation
->
link
));
if
(
$p1
&&
$p2
||
$p2
)
{
foreach
(
$relation
->
link
as
$a
=>
$b
)
{
$model
->
$a
=
null
;
}
$delete
?
$model
->
delete
()
:
$model
->
save
(
false
);
}
elseif
(
$p1
)
{
foreach
(
$relation
->
link
as
$b
)
{
$this
->
$b
=
null
;
}
$delete
?
$this
->
delete
()
:
$this
->
save
(
false
);
}
else
{
throw
new
Exception
(
''
);
}
}
}
if
(
!
$relation
->
multiple
)
{
if
(
!
$relation
->
multiple
)
{
...
@@ -1059,20 +990,35 @@ abstract class ActiveRecord extends Model
...
@@ -1059,20 +990,35 @@ abstract class ActiveRecord extends Model
}
}
/**
/**
* @param string $name
* @param array $link
* @return ActiveRelation
* @param ActiveRecord $foreignModel
* @param ActiveRecord $primaryModel
* @throws Exception
* @throws Exception
*/
*/
p
ublic
function
getRelation
(
$name
)
p
rivate
function
bindModels
(
$link
,
$foreignModel
,
$primaryModel
)
{
{
$getter
=
'get'
.
$name
;
foreach
(
$link
as
$fk
=>
$pk
)
{
try
{
$value
=
$primaryModel
->
$pk
;
$relation
=
$this
->
$getter
();
if
(
$value
===
null
)
{
if
(
$relation
instanceof
ActiveRelation
)
{
throw
new
Exception
(
'Primary Key is null'
);
return
$relation
;
}
}
}
catch
(
BadMethodException
$e
)
{
$foreignModel
->
$fk
=
$value
;
}
}
throw
new
Exception
(
'Unknown relation: '
.
$name
);
$foreignModel
->
save
(
false
);
}
/**
* @param array $keys
* @return boolean
*/
private
function
isPrimaryKey
(
$keys
)
{
$pks
=
$this
->
primaryKey
();
foreach
(
$keys
as
$key
)
{
if
(
!
in_array
(
$key
,
$pks
,
true
))
{
return
false
;
}
}
return
true
;
}
}
}
}
tests/unit/data/ar/Customer.php
View file @
46ab456f
...
@@ -15,7 +15,7 @@ class Customer extends ActiveRecord
...
@@ -15,7 +15,7 @@ class Customer extends ActiveRecord
public
function
getOrders
()
public
function
getOrders
()
{
{
return
$this
->
hasMany
(
'Order'
,
array
(
'customer_id'
=>
'id'
));
return
$this
->
hasMany
(
'Order'
,
array
(
'customer_id'
=>
'id'
))
->
orderBy
(
'id'
)
;
}
}
/**
/**
...
...
tests/unit/data/ar/Order.php
View file @
46ab456f
...
@@ -33,4 +33,14 @@ class Order extends ActiveRecord
...
@@ -33,4 +33,14 @@ class Order extends ActiveRecord
->
viaTable
(
'tbl_order_item'
,
array
(
'order_id'
=>
'id'
))
->
viaTable
(
'tbl_order_item'
,
array
(
'order_id'
=>
'id'
))
->
where
(
array
(
'category_id'
=>
1
));
->
where
(
array
(
'category_id'
=>
1
));
}
}
public
function
beforeSave
(
$insert
)
{
if
(
parent
::
beforeSave
(
$insert
))
{
$this
->
create_time
=
time
();
return
true
;
}
else
{
return
false
;
}
}
}
}
\ No newline at end of file
tests/unit/data/mysql.sql
View file @
46ab456f
...
@@ -43,7 +43,8 @@ CREATE TABLE `tbl_order` (
...
@@ -43,7 +43,8 @@ CREATE TABLE `tbl_order` (
`customer_id`
int
(
11
)
NOT
NULL
,
`customer_id`
int
(
11
)
NOT
NULL
,
`create_time`
int
(
11
)
NOT
NULL
,
`create_time`
int
(
11
)
NOT
NULL
,
`total`
decimal
(
10
,
0
)
NOT
NULL
,
`total`
decimal
(
10
,
0
)
NOT
NULL
,
PRIMARY
KEY
(
`id`
)
PRIMARY
KEY
(
`id`
),
CONSTRAINT
`FK_order_customer_id`
FOREIGN
KEY
(
`customer_id`
)
REFERENCES
`tbl_customer`
(
`id`
)
ON
DELETE
CASCADE
)
ENGINE
=
InnoDB
DEFAULT
CHARSET
=
utf8
;
)
ENGINE
=
InnoDB
DEFAULT
CHARSET
=
utf8
;
CREATE
TABLE
`tbl_order_item`
(
CREATE
TABLE
`tbl_order_item`
(
...
...
tests/unit/framework/db/ActiveRecordTest.php
View file @
46ab456f
...
@@ -8,6 +8,7 @@ use yiiunit\data\ar\ActiveRecord;
...
@@ -8,6 +8,7 @@ use yiiunit\data\ar\ActiveRecord;
use
yiiunit\data\ar\Customer
;
use
yiiunit\data\ar\Customer
;
use
yiiunit\data\ar\OrderItem
;
use
yiiunit\data\ar\OrderItem
;
use
yiiunit\data\ar\Order
;
use
yiiunit\data\ar\Order
;
use
yiiunit\data\ar\Item
;
class
ActiveRecordTest
extends
\yiiunit\MysqlTestCase
class
ActiveRecordTest
extends
\yiiunit\MysqlTestCase
{
{
...
@@ -190,6 +191,86 @@ class ActiveRecordTest extends \yiiunit\MysqlTestCase
...
@@ -190,6 +191,86 @@ class ActiveRecordTest extends \yiiunit\MysqlTestCase
$this
->
assertEquals
(
1
,
count
(
$customers
[
1
]
->
orders
[
1
]
->
items
));
$this
->
assertEquals
(
1
,
count
(
$customers
[
1
]
->
orders
[
1
]
->
items
));
}
}
public
function
testLink
()
{
$customer
=
Customer
::
find
(
2
);
$this
->
assertEquals
(
2
,
count
(
$customer
->
orders
));
// has many
$order
=
new
Order
;
$order
->
total
=
100
;
$this
->
assertTrue
(
$order
->
isNewRecord
);
$customer
->
link
(
'orders'
,
$order
);
$this
->
assertEquals
(
3
,
count
(
$customer
->
orders
));
$this
->
assertFalse
(
$order
->
isNewRecord
);
$this
->
assertEquals
(
3
,
count
(
$customer
->
getOrders
()
->
all
()));
$this
->
assertEquals
(
2
,
$order
->
customer_id
);
// belongs to
$order
=
new
Order
;
$order
->
total
=
100
;
$this
->
assertTrue
(
$order
->
isNewRecord
);
$customer
=
Customer
::
find
(
1
);
$this
->
assertNull
(
$order
->
customer
);
$order
->
link
(
'customer'
,
$customer
);
$this
->
assertFalse
(
$order
->
isNewRecord
);
$this
->
assertEquals
(
1
,
$order
->
customer_id
);
$this
->
assertEquals
(
1
,
$order
->
customer
->
id
);
// via table
$order
=
Order
::
find
(
2
);
$this
->
assertEquals
(
0
,
count
(
$order
->
books
));
$orderItem
=
OrderItem
::
find
(
array
(
'order_id'
=>
2
,
'item_id'
=>
1
));
$this
->
assertNull
(
$orderItem
);
$item
=
Item
::
find
(
1
);
$order
->
link
(
'books'
,
$item
,
array
(
'quantity'
=>
10
,
'subtotal'
=>
100
));
$this
->
assertEquals
(
1
,
count
(
$order
->
books
));
$orderItem
=
OrderItem
::
find
(
array
(
'order_id'
=>
2
,
'item_id'
=>
1
));
$this
->
assertTrue
(
$orderItem
instanceof
OrderItem
);
$this
->
assertEquals
(
10
,
$orderItem
->
quantity
);
$this
->
assertEquals
(
100
,
$orderItem
->
subtotal
);
// via model
$order
=
Order
::
find
(
1
);
$this
->
assertEquals
(
2
,
count
(
$order
->
items
));
$this
->
assertEquals
(
2
,
count
(
$order
->
orderItems
));
$orderItem
=
OrderItem
::
find
(
array
(
'order_id'
=>
1
,
'item_id'
=>
3
));
$this
->
assertNull
(
$orderItem
);
$item
=
Item
::
find
(
3
);
$order
->
link
(
'items'
,
$item
,
array
(
'quantity'
=>
10
,
'subtotal'
=>
100
));
$this
->
assertEquals
(
3
,
count
(
$order
->
items
));
$this
->
assertEquals
(
3
,
count
(
$order
->
orderItems
));
$orderItem
=
OrderItem
::
find
(
array
(
'order_id'
=>
1
,
'item_id'
=>
3
));
$this
->
assertTrue
(
$orderItem
instanceof
OrderItem
);
$this
->
assertEquals
(
10
,
$orderItem
->
quantity
);
$this
->
assertEquals
(
100
,
$orderItem
->
subtotal
);
}
public
function
testUnlink
()
{
// has many
$customer
=
Customer
::
find
(
2
);
$this
->
assertEquals
(
2
,
count
(
$customer
->
orders
));
$customer
->
unlink
(
'orders'
,
$customer
->
orders
[
1
]);
$this
->
assertEquals
(
1
,
count
(
$customer
->
orders
));
$this
->
assertNull
(
Order
::
find
(
3
));
// via model
$order
=
Order
::
find
(
2
);
$this
->
assertEquals
(
3
,
count
(
$order
->
items
));
$this
->
assertEquals
(
3
,
count
(
$order
->
orderItems
));
$order
->
unlink
(
'items'
,
$order
->
items
[
2
]);
$this
->
assertEquals
(
2
,
count
(
$order
->
items
));
$this
->
assertEquals
(
2
,
count
(
$order
->
orderItems
));
// via table
$order
=
Order
::
find
(
1
);
$this
->
assertEquals
(
2
,
count
(
$order
->
books
));
$order
->
unlink
(
'books'
,
$order
->
books
[
1
]);
$this
->
assertEquals
(
1
,
count
(
$order
->
books
));
$this
->
assertEquals
(
1
,
count
(
$order
->
orderItems
));
}
// public function testInsert()
// public function testInsert()
// {
// {
// $customer = new Customer;
// $customer = new Customer;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment