I. Preamble
Recently, I've been writing about the ThinkPHP associative model, and I've been forgetting some of the usage, so I thought I'd review and organize some of the usage by blogging about it.
Specific version:
- topthink/framework:6.1.4
- topthink/think-orm:2.0.61
II. Example applications
1、One-to-one association
1.1, I first designed two tables, respectively, the user table (user), user extension table (user_extend)
1.2 Modeling the two tables separately
<?php /** * Created by PhpStorm * Author: fengzi * Date: 2023/12/19 * Time: 14:50 */ namespace app\common\model; /** * :: User models */ class UserModel extends ComBaseModel { protected $name='user'; /** * The associated user extension table * The first parameter of hasOne is the name of the model class to be associated, the second parameter is the name of the foreign key of the association, and the third parameter is the name of the primary key of the current model (userModel) * @return \think\model\relation\HasOne * @Author: fengzi * @Date: 2024/6/27 17:38 */ public function userExtend() { return $this->hasOne(UserExtendModel::class,'user_id','id'); } }
<?php /** * Created by PhpStorm * Author: fengzi * Date: 2023/12/19 * Time: 14:50 */ namespace app\common\model; /** * :: User extension table */ class UserExtendModel extends ComBaseModel { protected $name='user_extend'; /** * Relative association of user models * The first parameter of belongsTo is the association model class name, the second parameter is the foreign key of the current model (UserExtendModel), and the third parameter is the primary key of the associated table. * @return \think\model\relation\BelongsTo * @Author: fengzi * @Date: 2024/6/27 17:41 */ public function user() { return $this->belongsTo(UserModel::class,'user_id','id'); } }
1.3. with() associative queries
One-to-one associative query, user table to user_extend table using hasOne, using hasWhere query user_extend table, is equivalent to the user_id collection in the user_extend table that meets the query condition as the query condition of the user table.
Because of the one-to-one association used, the user table will not have data when user_extend does not find the corresponding query data.
Attention:
- The first argument to hasWhere() must be the name of the method in the user model that associates the user extension table.
- The name in the associated model in the WITH preload query is the name of the method in the USER model that associates the USER EXTEND table, or it can be converted to an underscore if the method name is written in camel case. For example: userExtend => user_extend
<?php /** * Created by PhpStorm * Author: fengzi * Date: 2024/6/26 * Time: 17:13 */ namespace app\admin\controller\orm; use app\common\model\UserModel; class OrmController { /** * @var UserModel|object|\think\App */ private UserModel $userModel; public function __construct() { $this->userModel = app(UserModel::class); } public function index() { /** * with associative queries one-to-one * The value of the fourth joinType parameter of hasWhere: LEFT, RIGHT, or INNER. * user table to user_extend table using hasOne * Using hasWhere to query the user_extend table is equivalent to using the set of user_ids in the user_extend table that match the query criteria as the query criteria for the user table. * Because of the one-to-one association, when user_extend doesn't find the corresponding query data, the user table won't have any data either. */ $lists = $this->userModel ->with(['user_extend']) ->hasWhere('userExtend', function ($query){ $query->where('organize_id', 1); }, '*', 'LEFT') ->select()->toArray(); // output printing dd($lists); } }
1.3.1 Display of query results
1.4, withJoin() query (I defined the user_extend table in the user model with the association method name userExtend):
- withJoin(['user_extend"]): the associated table data queried in this way will be at the same level as the main table data. And the related table data will be presented in the form of prefix + underscore + table field with the name of the related method defined in the user model.
- Note: The underscore in [userExtend__xxx] is two, not one underscore (one underscore _; two underscores __ ). So be careful when you go to get the field data after the query result object is converted to an array to avoid reporting errors.
- This write sibling has an additional [user_extend] field with a value of null. this field does not exist in either datasheet.
- withJoin(['userExtend"]): In this way, the data of the related table is parent-child relationship with the data of the main table. The model name "userExtend" defined in withJoin is used as a field in the main table, and the data of the related table is put into this field.
public function index() { // instantiated model $userModel = app(UserModel::class); /** * :: Joint table data and main table data are at the same level */ $info = $userModel->withJoin(['user_extend'])->find(1); dump($info->toArray()); /** * :: Linked table data is a child of the main table data, and the main/linked table data show a parent-child relationship */ $info = $userModel->withJoin(['userExtend'])->find(1); dd($info->toArray()); }
1.4.1, Conditional Query Considerations for withJoin:
- If you use withJoin() to make an association, you can just use where() method to filter the conditions when you make a conditional query.
- If there are the same fields in the main/joint table and the filter condition is the same field, using the hasWhere() method for conditional filtering will report an error.
- If there are the same fields in the main/joint table and the filter criteria aremain tableIn the field in the field, then the where condition should be written like this [where(['mkl_user.role_id'=>2])]. At the same time, the role_id in the parameter should state which table it is, and take the table prefix.
- If there are the same fields in the main/joint table and the filter criteria areleague tableIf you want to use a field in the table, then the where condition should be written like this [where(['userExtend.role_id'=>3])]. At the same time, the role_id in the parameter should state which associated table is associated with the name withJoin('userExtend')].
public function index() { // instantiated model $userModel = app(UserModel::class); /** * where(['organize_id'=>3]) * The results can be queried based on the conditions * * * hasWhere('userExtend', function ($query){ * $query->where('organize_id', 3); * }, '*', 'LEFT') * Can't get results, error "Illegal offset type". */ $info = $userModel->withJoin(['user_extend']) ->where(['organize_id'=>3]) /*->hasWhere('user_extend', function ($query){ $query->where('organize_id', 3); })*/ ->select(); /*********** The following is the query when the primary/join table has the same field (same role_id field) ***********/
// To filter the same fields in the main table (user table) $info = $userModel ->withJoin('userExtend') ->where(['mkl_user.role_id'=>2]) ->select(); // output printing dump($info->toArray()); // To filter the same fields in a related table (user_extend table) $extendInfo = $userModel ->withJoin('userExtend') ->where(['userExtend.role_id'=>3]) ->select(); // output printing dd($extendInfo->toArray()); }
1.5. with association modification
There are two common forms of association modification:
- Way 1: Write the data to be modified in the associated table into an array and use the save method to modify it directly.
- Way 2: After assigning values to individual fields one by one, finally use the save method to make changes.
Attention:
- No matter which of the above, the queried object cannot be converted to an array and then modified for the associated table, or it will report an error.
- The name of the association in with should generally be consistent with the modification. Of course, it's okay to write the name of the associated method in the model if it's not consistent. For example, if you use user_extend in with, you can use userExtend in modify.
public function index() { // Related queries $info = $this->userModel->with(['user_extend'])->find(1); // output printing dump($info->toArray()); // Associated modifications: Mode I /*$userExtend = [ 'email' => '88888888@', 'gender' => 1, ]; $info->user_extend->save($userExtend);*/ // Associated modifications: Mode II $info->userExtend->email = '88888888@'; $info->userExtend->gender = 0; $info->userExtend->save(); // Re-associate the query $newInfo = $this->userModel->with(['user_extend'])->find(1); // output printing dd($newInfo->toArray()); }
1.5.1, with associated modified results
1.6 Deletion by association
Associative deletion here are some small details need to pay attention to, below we use user_id = 1 user to test. The following user_id=1 user data is currently not deleted.
1.6.1 Deletion of with associations
(1) This way of writing can correctly delete the data of the main table and the related table, and it should be noted that the parameter names in [with(['userExtend'])] and [together(['userExtend'])] should be the same, and there should not be one hump [userExtend] writing style and another underscore [user_extend] writing style. . Either both write [userExtend] or both write [user_extend].
public function index() { // instantiated model $userModel = app(UserModel::class); // Query data with user ID=1 $info = $userModel->with(['userExtend'])->find(1); // Deleting Primary and Related Tables $del = $info->together(['userExtend'])->delete(); }
(2) This way of writing does not have with() method in the query, so when deleting the data, only the data of the main table will be deleted, but the data of the related table will not be deleted.
public function index() { // instantiated model $userModel = app(UserModel::class); // Query data with user ID=1 $info = $userModel->find(1); // Deleting Primary and Related Tables $del = $info->together(['userExtend'])->delete(); // Output printing dd($del); }
Note: Calling together() to delete data from the main/joint table after the data in the main table has already been deleted will result in an error.
(3) The current code in the way one and way two is actually not much different from the above introduction, just the chaining operation of the above several writings.
- Way 1: Only the main table data can be deleted, the related table data will not be deleted.
- Way 2: You can delete data from both the main table and the related table.
public function index() { // instantiated model $userModel = app(UserModel::class);// Deleting Primary and Related Tables // Method 1 $del = $userModel->find(1)->together(['userExtend'])->delete(); // Mode 2 $del = $userModel->with(['userExtend'])->find(1)->together(['userExtend'])->delete(); }
1.6.2 Deletion of association withJoin
Attention:
- If you want all the data of the main/joint table to be deleted, then you must bring the withJoin() method in the query.
- After querying with the withJoin() method, the parameter of the together() and withJoin() methods at the time of deletion must be the name of the associated method in the UserModel model, i.e., the name of the userExtend() method at 1.2 of this document.
- If you want to delete the data of the main/join table after querying without withJoin() method, it is not feasible, then the deletion will only delete the data of the main table, and the data of the join table will not be deleted.
public function index() { // instantiated model $userModel = app(UserModel::class); /** * withJoin(['user_extend'])together(['userExtend']) main table data deleted, join table data will not be deleted * withJoin(['user_extend'])together(['user_extend']) Main table data is deleted, join table data is not deleted. * withJoin(['userExtend'])together(['userExtend']) Main table data is deleted, join table data is deleted. * withJoin(['userExtend'])together(['user_extend']) Main table data is deleted, join table data is not deleted. */ // Query data with user ID=1 $info = $userModel->withJoin(['userExtend'])->find(1); // Deleting Primary and Related Tables $del = $info->together(['userExtend'])->delete();
/** * together(['user_extend']) main table data deleted, join table data will not be deleted * together(['userExtend']) main table data is deleted, join table data will not be deleted. */ // Query data with user ID=1 $info = $userModel->find(1); // Deleting Primary and Related Tables $del = $info->together(['userExtend'])->delete(); }