Write operations
Index
Create new model
create
: Create a new instance and insert it to database.new
: Creat a new instance. (without inserting it to database)
After insert, auto-update timestamps of the instance will not be updated automatically. But auto-incremented values will be updated. If you want to read timestamp values, use refresh
.
create
Create a model from attributes. create
method creates a new instance and insert it to database.
$attr = ['id' => 0, 'name' => 'abc'];
$customer = Customer::create($attr);
firstOrCreate
Search the object in the database with the specified attributes, and return it. If it can not be found, the new instance will be created and inserted to the database.
$attr = ['id' => 1, 'name' => 'abc'];
// Use primary key if there is no index number specified.
$customer = Customer::firstOrCreate($attr);
firstOrNew
Search the object in the database with the specified attributes, and return it. If it can not be found, the new instance will be created (without inserting it to database).
$attr = ['id' => 1, 'name' => 'abc'];
// Use primary key if there is no index number specified.
$customer = Customer::firstOrNew($attr);
Check attributes
Using values received from HTTP requests as the parameters of create
method may causes passing invalid values. This can be a serious security hole.
Therefore, we must use only predetermined attributes. We can use either the white-list or the black-list to predetermine them.
- white-list :
static $fillable
proterty - black-list :
static $guarded
proterty
If the white-list is specified, attributes not in white-list will be ignored.
class Customer extends Model
{
protected static $fillable = ['id', 'name'];
}
If the black-list is specified, attributes in black-list will be ignored.
class Customer extends Model
{
protected static $guarded = ['note'];
}
Save and delete models
Save
Save a model to the database. Auto-update timestamps of the model will not be updated automatically. But auto-incremented values will be updated.
$attr = ['id' => 1, 'name' => 'abc'];
$customer = Customer::firstOrNew($attr);
$customer->save();
save
tries to update the record by primary key first, then insert it if it has failed.
Set belongs-to values by object
To associate the belongs-to object, use associate
method of Relation
objects.
$group = Group::find(1);
$customer = new Customer();
$customer->id = 0;
$customer->name = 'John';
$customer->group()->associate($group); // Copy $group->id to $customer->group_id.
$customer->save();
Delete
Delete the model from the database. An instance of the model will not be unset
.
$customer = Customer::find(1);
$customer->delete();
The primary key will be used to delete.
Save with the associated models
Normally, save
or delete
does not affect the associated models. By specifying Model::SAVE_WITH_RELATIONS
for $option
parameter of save
or delete
method, you can save object with the associated models.
Note:
- It does not affect recursively.
- Except objects not loaded.
- The associated object can also be
Collection
object.
save
is done in order, main object first, then associated objects.
$customer = Customer::find(1);
$customer->name = 'Akio';
$customer->address->street = '3-1-5 igawazyo';
// Update both of customer and address.
$customer->save(Model::SAVE_WITH_RELATIONS);
Collections
You can store models collectively with save
method of Collection
.
$grp = new Group();
$grp->id = 0;
$grp->name = 'Tokyo';
$customer1 = new Customer();
$customer1->id = 0;
$customer1->name = 'John';
$grp->customers->add($customer1);
$customer2 = new Customer();
$customer2->id = 0;
$customer2->name = 'Mike';
$grp->customers->add($customer2);
// Save $grp and customers in collection.
$grp->save(Model::SAVE_WITH_RELATIONS);
many-to-many Relation Models
To save only the added relation objects, you can use save
method of Relation
objects.
$customer = Customer::find(1);
$tag = Tag::find(1);
$customer->tags()->save($tag);
When you save the collection, related intermediate objects will be saved in the intermediate table automatically.
$customer = Customer::find(1);
$tag = Tag::find(1);
$tags = $customer->tags;
// Currentry $tags has 2 tags
$tags->add($tag);
DB::beginTransaction();
$tags->save(); // Saved 3 tags.
DB::commit();
If another user adds a tag to the same customer after you had read the tags of the customer, it may cause problems.
Collection::save
method can delete items from the database before saving the collection, according to the conditions of the relationship. You can save perfectly with this way.
However, if you do not need to delete them, specify SAVE_BEFOERE_NO_DELETE
for $saveOprions
. It improves performance.
$tags->saveOprions = Collection::SAVE_BEFOERE_NO_DELETE;
$tags->save();
Update Conflict Check (UCC)
If there is a timestamp field that is updated automatically on updating, Transactd PHP ORM can detect update conflicts by multiple users.
- MySQL 5.6 or later, or, MariaDB 5.5 or later is required.
- Detection is enabled on
update
ordelete
, only for single record operation.
$customer = Customer::find(1);
if ($customer->setUpdateConflictCheck(true) === false) {
echo 'This table does not have timestamp fields.';
} else {
// (omitted)
// If another user updated the same customer (id = 1)...
try {
$customer->delete(); // IOException will be thrown.
}
catch(Transactd\IOException $e) {
echo $e->getCode() . ':' . $e->getMessage() . PHP_EOL;
}
}
Note: The automatic update timestamp of MySQL/MariaDB is recorded up to microsecond like 2016-12-06 11:49:26.466757
. The times of two or more updates serialized by locks almost never become the same, so it is possible to detect updating.
However, this is not a 100% sure way. The time stamp is based on the system time, but the system time is changed by the user, NTP, etc. It also depends on OS and machine resolution.
If 100% certainty is required, add unsigned integer
column such as named version
, increment it every updating, and compare version
instead of update_at
. There is an example in Detect change conflict section in sample application.
ServerCursor
Get the current server cursor from a model.
serverCursor
method in queryExecuter
requires keyValue
, but the method in the model does not require it because the model holds key and value. It is necessary to specify index
, but it is added to the first parameter of serverCursor
, not the method. If the default value null
is specified, it is automatically set to primary key
.
The prototype and usage examples are as follows:
serverCursor($index = null, $op = QueryExecuter::SEEK_EQUAL, $lockBias = Transactd::LOCK_BIAS_DEFAULT, $forword = true)
$scr = $customer->serverCursor();
$customer->name = 'akio';
$scr->update($customer);
See QueryExecuter - ServerCursor for more detail.
Use Transaction
It is required that open all tables which will be used before transaction. To open these tables, use prepareTable
method.
$grp = new Group();
$grp->id = 0;
$grp->name = 'Tokyo';
$customer1 = new Customer();
$customer1->id = 0;
$customer1->name = 'John';
$grp->customers->add($customer1);
$customer2 = new Customer();
$customer2->id = 0;
$customer2->name = 'Mike';
$grp->customers->add($customer2);
// Prepare tables.
Customer::prepareTable();
Group::prepareTable();
DB::beginTransaction();
$grp->save(Model::SAVE_WITH_RELATIONS);
DB::commit();
Note: Transactions are valid only on one connection.
Events of write operations
In the writing process, you can handle two events, before writing and after writing. An event handler is a static function with following name:
creating
,created
updating
,updated
saving
,saved
deleting
,deleted
save
operation does not fire create
or update
events. It fires save
events only.
Define event handlers
Define creating
and created
.
class Customer extends Model
{
public static function creating($customer)
{
echo 'Creating a customer id = ' . $customer->id . PHP_EOL;
return true; // continue
}
public static function created($customer)
{
echo 'Created a customer id = ' . $customer->id . PHP_EOL;
}
}
If the pre-processing handler returns false
, processing will be aborted.