model

Introduction

Whether it is an advanced query or an underlying query, you will need a table entity. A table field and a class attribute are one-to-one mapping. The operation of the class is equivalent to the operation of the table. The class is called an entity. The Swaft 2.x and 1.x entities are much simpler to use, and both have the Builder query constructor. The method uses the same method as the query constructor, except that the return may become an entity.

entity

An entity class corresponds to a table structure of a database, and an entity object represents a row of data records of the table.

Note: Entities cannot be injected into any class as attributes because each entity object is a different data record line. The entity object is where it is created and where it is created.

Entity definition

Let's look at an example of entity definition:

 <?php declare(strict_types=1);

 namespace SwoftTest\Db\Testing\Entity;

 use Swoft\Db\Annotation\Mapping\Column;
 use Swoft\Db\Annotation\Mapping\Entity;
 use Swoft\Db\Annotation\Mapping\Id;
 use Swoft\Db\Eloquent\Model;

 /**
  * Class User
  *
  * @since 2.0
  *
  * @Entity(table="user",pool="db.pool2")
  */
 class User extends Model
 {
     /**
      * @Id(incrementing=true)
      *
      * @Column(name="id", prop="id")
      * @var int|null
      */
     private $id;

     /**
      * @Column(name="password", hidden=true)
      * @var string|null
      */
     private $pwd;

     /**
      * @Column()
      *
      * @var int|null
      */
     private $age;

     /**
      * @Column(name="user_desc", prop="udesc")
      *
      * @var string|null
      */
     private $userDesc;

    /**
     * @return string|null
     */
     public function getUserDesc(): ?string
     {
        return $this->userDesc;
     }

    /**
     * @param string|null $userDesc
     */
     public function setUserDesc(?string $userDesc): void
     {
        $this->userDesc = $userDesc;
     }

     /**
      * @return int|null
      */
     public function getId(): ?int
     {
         return $this->id;
     }

     /**
      * @param int|null $id
      */
     public function setId(?int $id): void
     {
         $this->id = $id;
     }

     /**
      * @return int|null
      */
     public function getAge(): ?int
     {
         return $this->age;
     }

     /**
      * @param int|null $age
      */
     public function setAge(?int $age): void
     {
         $this->age = $age;
     }

     /**
      * @return string|null
      */
     public function getPwd(): ?string
     {
         return $this->pwd;
     }

     /**
      * @param string|null $pwd
      */
     public function setPwd(?string $pwd): void
     {
         $this->pwd = $pwd;
     }
 } 

You can also use the fast build 生成实体 tool in devtool

If the @Column column is not defined, the @Column value that does not exist with the insert/update will be automatically filtered by the framework.

Annotation label

@Entity

Mark a class as an entity with two parameters

  • Name Defines the database table name for this entity mapping (required)
  • The connection pool selected by this entity defaults to db.pool You can replace it with your own defined connection pool. There are two purposes for designing this parameter. The first one can switch itself to define the db connection pool. The second is that you can use your own. Implemented database driver,

If the User table is MySQL , the Count table can be PostSQL using PostSQL using different connection pools.

@Column

Mark a column. If a column does not have @Column defined, it will not be displayed, so even if you add a database field, it will not affect the production environment.

  • Name Defines the table field of the class attribute mapping. The attribute without the annotation tag is not mapped (the default is the field name).
  • Prop sets an alias for the field

    Prop just sets an alias for the field and will only be converted when toArray is called. This will hide the real fields of the database. To use a clause such as where need to use a database field.

  • Hidden is hidden. If it is true then it will be hidden when it is toArray() , but it does not affect you to get it through Getter . You can also addVisible it by calling the entity's addVisible method.

    Description: All field properties must have getter and setter methods. You can use the phpstorm shortcut ctrl+n , which will generate getter and setter faster with more properties.

Note If the table field is underlined, the class attribute is defined as the 小驼峰 writing case: the field user_name is written as $userName

2.x removes the type attribute and now uses the first type defined by the @var annotation defined on the attribute, which determines the return value type.

@Id

The annotation indicates that the current class attribute corresponds to the primary key in the database table, and must have this annotation tag. You cannot set multiple @Id annotations.

  • Whether incrementing is an incremental primary key, the default is to increment the primary key.

Prop operation

2.0.6 support

Model insertion support using prop insert

For example, in the above example, the real database field is user_desc , the prop field is udesc , and the bottom layer is automatically converted to user_desc

Of course this does not affect the previous use

 User::new([
    'udesc' => $desc,
])->save(); 

The condition uses prop , using the whereProp method, whereProp method can be used with where .

 
$where      = [
    'pwd' => md5(uniqid()),
    ['udesc', 'like', 'swoft%'],
    ['whereIn', 'id', [1]]
];

// 'select * from `user` where (`password` = ? and `user_desc` like ? and `id` in (?))';
$sql = User::whereProp($where)->toSql(); 

where extension is used, each element in the data, is the method name, supports all the methods related to Where in the Query Builder ,

 $toSql = 'select * from `user` where (`id` in (?) or `id` = ? or `status` > ? and `age` between ? and ?)';
$where = [
    ['whereIn', 'id', [1]],
    ['orWhere', 'id', 2],
    ['orWhere', 'status', '>', -1],
    ['whereBetween', 'age', [18, 25]]
];
$sql   = User::where($where)->toSql();
// same as
User::where('id', '=', [1])
            ->orWhere('id', 2)
            ->orWhere('status', '>', -1)
            ->whereBetween('age', [18, 25]) 

Insert data

Object mode insertion to obtain self-incrementing Id

 $user = User::new();
$user->setName('name');
$user->setSex(1);
$user->setDesc('this my desc');
$user->setAge(mt_rand(1, 100));
$user->save();
// saved after getId()  
$id = $user->getId(); 

Array mode

 $attributes = [
    'name'      => uniqid(),
    'password'  => md5(uniqid()),
    'age'       => mt_rand(1, 100),
    'user_desc' => 'u desc'
];
$user  = User::new($attributes)

$result3 =$user->save();

$id = $user->getId() 

After adding save , you can use the getter method to get the self-increment id.

Bulk insert

If you want to bulk insert you can use the User::insert([]) method to use exactly the same as the insert constructor of the query constructor.

delete data

Specify id to delete

 $user = User::find($id);
$result = $user->delete(); 

Use conditional deletion

 $result = User::where('id', 1)->delete(); 

Delete one

 $result = User::where('stauts',1 )->limit(1)->delete(); 

Entity update

Can be updated using setter or array

 $user = User::find($id);

$name   = uniqid();
$user->setAge(1);

$result = $user->update(['name' => $name]); 

Conditional batch update

Update a piece of data

 $wheres   = [
    'name' => 'swoft',
    ['id', '>=', 2]
];
$orWheres = [
    ['status', '<>', '1']
];
$result   = User::where($wheres)
                ->limit(1)
                ->orWhere($orWheres)
                ->update(['status' => 1]); 

Update/insert

Can use updateOrCreate to return an entity

 $user = User::updateOrCreate(['id' => 1], ['age' => 18, 'name' => 'sakuraovq']);
echo $user->getName(); 

You can also use updateOrInsert to return a bool value.

 $isOk = User::updateOrInsert(['id' => 1], ['age' => 18, 'name' => 'sakuraovq']); 

Batch update using primary key

In this example id is the @Id() primary key of the User entity.

 $values = [
    ['id' => 1, 'age' => 18],
    ['id' => 2, 'age' => 19],
];

User::batchUpdateByIds($values); 

The bulk update must specify the value of the primary key, and the framework will update the batch based on the value of the primary key.

Quick update

If you already know the updated primary key id can be updated quickly using the modifyById method

   // method 1
  $row = User::modifyById($id, ['user_desc' => $expectLabel]);
  // method 2
  User::find($id)->update(['user_desc' => $expectLabel]); 

If you don't know the primary key id that needs to be updated, you can use the modify method. This method will first find the id according to the condition and then update

 
   $where  = ['user_desc' => 'CP'];
   $values = ['user_desc' => $expectLabel];

   // method 1
   $row = User::modify($where, $values);
   // method 2
   $model = User::where($where)->first()->update($values); 

Method 1 and Method 2 are the same meaning

Increment/decrement

Single field increment/decrement

Use increment decrement decrement , the third parameter is the value with the update

     $updateByWhereId = User::where('id', 1)->increment('age', 1);
    $updateByModel   = User::find(1)->decrement('age', 2); 

Multiple fields increment/decrement

Use the updateAllCounters method to update. Note that please use the update conditions with caution, preferably using the primary key update to avoid table locks

    User::updateAllCounters(['user_desc' => $expectLabel], ['age' => -1]); 

If you know the primary key id you need to update, you can use the updateAllCountersById method.

     // method 1
    User::updateAllCountersById((array)$id, ['age' => 1], ['user_desc' => $expectLabel]);

    // method 2
    User::find($id)->updateCounters(['age' => -1]); 

Query data

The model's query method is fully compatible with the query constructor

Query a piece of data and return an entity

 // 方法 1
$user =  User::find(1, ['id','name']);
// 方法 2
$user = User::where('id',1)->first(); 

Query multiple data

 // 方法 1
$users = User::findMany([1,2,3,4], ['id','name']);
// 方法 2
$useer = User::whereIn('id', [1,2,3,4])->select(['id','name'])->get(); 

If you want to get a list of 实体 objects you can use the getModels method, this method returns an array of entities you can use like this:

 $users = User::where('id', 22)->getModels(['id', 'age']);
/* @var User $user */
foreach ($users as $user) {
    $age = $user->getAge();
} 

Sometimes I need to press the database key as the key our logical mapping relationship, you can use the Collection 's keyBy method.

If you need the first page of data to use id as the key you can use it like this:

 $users = User::forPage(1, 10)->get(['id', 'age'])->keyBy('id');

/* @var User $user */
foreach ($users as $id => $user) {
    $age = $user->getAge();
} 

Entity uses Join series operations and does not return entities

 $userCounts = User::join('count', 'user.id', '=', 'count.user_id')->get(); 

Blocking result

If you need to process thousands of Eloquent records, you can use the chunk command. chunk method retrieves the "chunks" of the Eloquent model and provides them to the specified Closure for processing. When dealing with large result sets, use the chunk method to save memory:

  Flight::chunk(200, function ($flights) {
        foreach ($flights as $flight) {
            //
        }
 }); 

The first parameter passed to the method is the amount of data that you want each "block" to receive. The closure is passed as the second parameter, which is called each time a database query is passed to pass each block.

Using cursors

cursor allows you to use the cursor to traverse the database data, the cursor only execute a query. When dealing with large amounts of data, you can use the cursor method to dramatically reduce memory usage:

     foreach (Flight::where('foo', 'bar')->cursor() as $flight) {
        //
    } 

"can't find" exception

If you want to throw an exception when the model is not found, you can use the findOrFail and firstOrFail methods. These methods retrieve the first result of the query. If the corresponding result is not found, a DbException will be thrown:

     $model = App\Flight::findOrFail(1);

    $model = App\Flight::where('legs', '>', 100)->firstOrFail(); 

Assignment

If you think that the setter too cumbersome to use, the bulk fill function, use this method to note that if the field 没有匹配 the @Column value, it will be ignored to ensure safe update and insertion.

 // Properties
    $attributes = [
        'name'      => uniqid(),
        'password'  => md5(uniqid()),
        'age'       => mt_rand(1, 100),
        'user_desc' => 'u desc'
    ];
    // one 
    $result3 = User::new($attributes)->save();
    // two
    $result3 = User::new()->fill($attributes)->save(); 

Search collection

You can also use the count , sum , max and other aggregate functions provided by the query constructor . These methods only return the appropriate scalar value instead of the entire model instance:

     $count = App\Flight::where('active', 1)->count();

    $max = App\Flight::where('active', 1)->max('price'); 

Other creation methods

firstOrCreate / firstOrNew

You can also create two models using two methods: firstOrCreate and firstOrNew . firstOrCreate method looks up the record in the database using the given field and its value. If the model is not found in the database, the record is inserted using the attribute from the first parameter and the attribute from the optional second parameter.

firstOrNew method is similar to the firstOrCreate method, which looks for records matching the given attribute in the database. If the model is not found, a new model instance is returned. Note that in this case, the model returned by firstOrnew has not yet been saved to the database, you must manually call the save method to save it:

     // 通过 name 属性检索航班,当结果不存在时创建它...
    $flight = App\Flight::firstOrCreate(['name' => 'Flight 10']);

    // 通过 name 属性检索航班,当结果不存在的时候用 name 属性和 delayed 属性去创建它
    $flight = App\Flight::firstOrCreate(
        ['name' => 'Flight 10'], ['delayed' => 1]
    );

    // 通过 name 属性检索航班,当结果不存在时实例化...
    $flight = App\Flight::firstOrNew(['name' => 'Flight 10']);

    // 通过 name 属性检索航班,当结果不存在的时候用 name 属性和 delayed 属性实例化
    $flight = App\Flight::firstOrNew(
        ['name' => 'Flight 10'], ['delayed' => 1]
    ); 

updateOrCreate

You may also encounter situations where you want to update an existing model or create a new one if it doesn't exist. Swoft provides the updateOrCreate method to do this. Like the firstOrCreate method, the updateOrCreate method saves the model, so there is no need to call save() :

     // 如果有从奥克兰飞往圣地亚哥的航班,将价格设为 99 美元
    // 如果不存在匹配的模型就创建一个
    $flight = App\Flight::updateOrCreate(
        ['departure' => 'Oakland', 'destination' => 'San Diego'],
        ['price' => 99]
    ); 

For more methods, please refer to the query constructor.

Automatically write timestamp

By default, Eloquent will have two fields, created_at and updated_at , in the default data table. If you don't need to automatically update these two fields, you need to set the $modelTimestamps property to false in the model:

 <?php

    namespace App;

    use App\Model\Entity;

    class User
    {
        /**
         * Whether the model is automatically maintained with a timestamp
         *
         * @var bool
         */
        public $modelTimestamps = false;
    } 

If you need a custom timestamp format, set the $modelDateFormat property inside the model. This property determines how the date attribute should be stored in the database, and the format in which the model is serialized into an array or JSON:

 <?php
    class User
    {
        /**
         * The storage format of the model's date field
         *
         * @var string
         */
        protected $modelDateFormat = 'Y-m-d H:i:s';
    } 

If you need to customize the field name used to store the timestamp, you can do this by setting the CREATED_AT and UPDATED_AT constants in the model:

The timestamp supports the database int and timestamp types. The underlying layer is automatically judged according to the @var defined by the entity's attributes CREATED_AT and UPDATED_AT . The user does not need to worry about generating the timestamp format.

 <?php
    class User
    {
       protected const CREATED_AT = 'create_time';
       protected const UPDATED_AT = 'update_data';
    } 

Temporarily does not support annotations. Inherited Dao layer inheritance Entity temporarily unavailable.

If you want the framework to automatically maintain CREATED_AT and UPDATED_AT, then the fields corresponding to these two constants must have corresponding getters and setters.

event

Eloquent's model triggers several events that can be monitored during the model's lifecycle: creating , created , updating , updated , saving , saved , deleting , deleted . Events can easily execute code each time you save or update a particular model class in the database. Of course you can do it all through AOP .

The creating and created events are fired when the new model is first saved. If the model already exists in the database and the save method is called, the updating and updated events are fired. In both cases, the saving / saved event will fire.

The event name is swoft.model + model name + action name

  • The model name is the first letter defaults to lowercase, for example, the entity name SendMessage to listen to its " saving action" format is swoft.model.sendMessage.saving other models are similar.

Can monitor a model of saving operation action, you can also listen to all models of the saving action

  • Listening model single action
 <?php declare(strict_types=1);

namespace App\Listener;

use App\Model\Entity\User;
use Swoft\Event\Annotation\Mapping\Listener;
use Swoft\Event\EventHandlerInterface;
use Swoft\Event\EventInterface;

/**
 * Class UserSavingListener
 *
 * @since 2.0
 *
 * @Listener("swoft.model.user.saving")
 */
class UserSavingListener implements EventHandlerInterface
{
    /**
     * @param EventInterface $event
     */
    public function handle(EventInterface $event): void
    {
        /* @var User $user */
        $user = $event->getTarget();

        if ($user->getAge() > 100) {
            // stopping saving
            $event->stopPropagation(true);

            $user->setAdd(100);
        }
    }
}
 
  • Listen for individual actions of all models
 <?php declare(strict_types=1);

namespace App\Listener;

use App\Model\Entity\User;
use Swoft\Db\DbEvent;
use Swoft\Db\Eloquent\Model;
use Swoft\Event\Annotation\Mapping\Listener;
use Swoft\Event\EventHandlerInterface;
use Swoft\Event\EventInterface;

/**
 * Class RanListener
 *
 * @since 2.0
 *
 * @Listener(DbEvent::MODEL_SAVED)
 */
class ModelSavedListener implements EventHandlerInterface
{
    /**
     * @param EventInterface $event
     */
    public function handle(EventInterface $event): void
    {
        /* @var Model $modelStatic */
        $modelStatic = $event->getTarget();

        if ($modelStatic instanceof User) {
            // to do something....
        }

        // ....
    }
}
 

A list of public event names, all events can be found in the Swoft\Db\DbEvent class

Event Params Description
swoft.db.transaction.begin No parameters The transaction starts.
swoft.db.transaction.commit No parameters Transaction submission.
swoft.db.transaction.rollback No parameters The transaction is rolled back.
swoft.model.saving Target is the concrete operation entity class All entities hold events.
swoft.model.saved Target is the concrete operation entity class All entities save post events.
swoft.model.updating Target is the concrete operation entity class All entities update pre-events.
swoft.model.updated Target is the concrete operation entity class All entity update events.
swoft.model.creating Target is the concrete operation entity class All entities create pre-events.
swoft.model.created Target is the concrete operation entity class All entities create post events.
swoft.model.deleting Target is the concrete operation entity class All entities delete the previous event.
swoft.model.deleted Target is the concrete operation entity class The previous event is deleted after all entities.
swoft.db.ran Target is the connection object, parameter 1 = not pre-processed sql, parameter 2 = bound parameters All sql executed events, the connection returned by the event has been returned to the connection pool can only get its configuration information.
swoft.db.affectingStatementing Target is the connection object, parameter 1 = PDO statement being processed, parameter 2 = bound parameter Executing update and delete actions
swoft.db.selecting Target is the connection object, parameter 1 = PDO statement being processed, parameter 2 = bound parameter The query action is being executed.

If it is in 正在进行时(ing) , it is called $event->stopPropagation(true); 正在进行时(ing) in the listener event $event->stopPropagation(true); subsequent operations will terminate the result directly. The 过去式 stop is invalid.

FQA

It is best to use the select method using the model, don't use as or the query results and entity mapping may be problematic.

Values updated/inserted using the model's methods will be filtered. Values not defined @Column will be filtered.

/docs/2.x/en/db/model.html
progress-bar