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 $fillableproterty - black-list :
static $guardedproterty
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
Collectionobject.
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
updateordelete, 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,createdupdating,updatedsaving,saveddeleting,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.