zoukankan      html  css  js  c++  java
  • Yii2中多表关联查询(join、joinwith)

    我们用实例来说明这一部分

    表结构

    现在有客户表、订单表、图书表、作者表,

    • 客户表Customer   (id  customer_name)
    • 订单表Order      (id  order_name   customer_id   book_id)
    • 图书表           (id  book_name    author_id)
    • 作者表           (id  author_name)

    模型定义

    下面是这4个个模型的定义,只写出其中的关联

    Customer 

    class Customer extends yiidbActiveRecord{

       // 这是获取客户的订单,由上面我们知道这个是一对多的关联,一个客户有多个订单

        public function getOrders(){

            // 第一个参数为要关联的子表模型类名,

            // 第二个参数指定 通过子表的customer_id,关联主表的id字段

            return $this->hasMany(Order::className(), ['customer_id' => 'id']);

        }

    }

    Order 

    class Order extends yiidbActiveRecord{

        // 获取订单所属用户

        public function getCustomer(){

            //同样第一个参数指定关联的子表模型类名

            return $this->hasOne(Customer::className(), ['id' => 'customer_id']);

        }

        // 获取订单中所有图书

        public function getBooks(){

            //同样第一个参数指定关联的子表模型类名

            return $this->hasMany(Book::className(), ['id' => 'book_id']);

        }

    }

    Book 

    class Book extends yiidbActiveRecord{

        // 获取图书的作者

        public function getAuthor(){

            //同样第一个参数指定关联的子表模型类名

            return $this->hasOne(Author::className(), ['id' => 'author_id']);

        }

    }

    Author 

    class Autor extends yiidbActiveRecord{

    }

    hasMany、hasOne使用

    Yii2中的表之间的关联有2种,它们用来指定两个模型之间的关联。

    • 一对多:hasMany
    • 一对一:hasOne
    • 返回结果:这两个方法的返回结果都为yiidbActiveQuery对象
    • 第一个参数:所关联的模型的类名称。
    • 第二个参数:是一个数组,其中键为所关联的模型中的属性,值为当前模型中的属性。

    关联的使用

    现在我们获取一个客户的所有的订单信息

    $customer = Customer::findOne(1);// 获取一个客户信息

    $orders = $customer->orders;  // 通过在Customer中定义的关联方法(getOrders())来获取这个客户的所有的订单。

    上面的两行代码会生成如下sql语句

    1.SELECT * FROM customer WHERE id=1;

    2.SELECT * FROM order WHERE customer_id=1; 

    关联结果缓存

    如果客户的订单改变了,我们再重新调用

    $orders = $customer->orders;

    再次得到订单的时候你会发现没有变化。原因是只会在第一次执行$customer->orders的时候才会去数据库里面查询,然后会把结果缓存起来,以后查询的时候都不会再执行sql。

    那么如果我想再次执行sql如何做呢?可以执行

    unset($customer->orders);

    $customer->orders;

    然后就可以从数据库里面取数据了。

    定义多个关联

    同样,我们还可以在Customer里面定义多个关联。
    如返回总数大于100的订单。

    class Customer extends yiidbActiveRecord{

       public function getBigOrders($threshold = 100){

            return $this->hasMany(Order::className(), ['customer_id' => 'id'])

                ->where(‘subtotal > :threshold’, [':threshold' => $threshold])

                ->orderBy(‘id’);

        }

    }

    关联的两种访问方式

    如上面的,如果使用

    $customer->bigOrders

    将会得到大于100的所有的订单。如果要返回大于200的订单可以这样写

    $orders = $customer->getBigOrders(200)->all();

    从上面可以看出访问一个关联的时候有两种方法

    • 如果以函数的方式调用,会返回一个 ActiveQuery 对象($customer->getOrders()->all())
    • 如果以属性的方式调用,会直接返回模型的结果($customer->orders)

    with的使用

    看如下代码,是取一个客户的订单

    $customer = Customer::findOne(1);// 执行sql语句: SELECT * FROM customer WHERE id=1

    $orders1 = $customer->orders;//执行sql:SELECT * FROM order WHERE customer_id=1

    $orders2 = $customer->orders;//这个不会执行sql,直接使用上面的缓存结果

    如果现在我们要取出100个用户,然后访问每个用户的订单,从上面的了解我们可能会写出如下代码

    // 执行sql语句: SELECT * FROM customer LIMIT 100

    $customers = Customer::find()->limit(100)->all();

    foreach ($customers as $customer) {

       $orders = $customer->orders; // 执行sql: SELECT * FROM order WHERE customer_id=…

       //处理订单。。。

    }

    然而,如果真要这样写的话,会在foreach的每个循环里面都执行一次sql去数据库里面查询数据。因为每个$customer对象都是不一样的。

    为了解决上面的问题 可以使用 yiidbActiveQuery::with()。
    其中with的参数为关系的名称,也就在model里面定义的getOrders,getCustomer中的orders和customer

    //先执行sql: SELECT * FROM customer LIMIT 100;

    //SELECT * FROM orders WHERE customer_id IN (1,2,…)

    $customers = Customer::find()->limit(100)->with(‘orders’)->all();

    foreach ($customers as $customer) {

        $orders = $customer->orders;// 在这个循环的时候就不会再执行sql了

       // …handle $orders…

    }

    如果使用了select来指定返回的列,一定要确保返回的列里面包含所关联的模型的关联字段,否则将不会返回关联的表的Model

    $orders = Order::find()->select(['id', 'amount'])->with(‘customer’)->all();

    // $orders[0]->customer 的结果将会是null

    // 因为上面的select中没有返回所关联的模型(customer)中的指定的关联字段。

    // 如果加上customer_id,$orders[0]->customer就可以返回正确的结果

    $orders = Order::find()->select(['id', 'amount', 'customer_id'])->with(‘customer’)->all(); 

    给with加过滤条件

    查询一个客户大于100的订单

    //首先执行sql: SELECT * FROM customer WHERE id=1

    $customer = Customer::findOne(1);

    // 再执行查询订单的sql语句:SELECT * FROM order WHERE customer_id=1 AND subtotal>100

    $orders = $customer->getOrders()->where(‘subtotal>100′)->all();

    查询100个客户的,每个客户的总和大于100的订单

      // 下面的代码会执行sql语句:

      // SELECT * FROM customer LIMIT 100

      // SELECT * FROM order WHERE customer_id IN (1,2,…) AND subtotal>100

      $customers = Customer::find()->limit(100)->with([

         'orders' => function($query) {

             $query->andWhere('subtotal>100');

         },

      ])->all();

    在这里with的参数为数组,键为关联的名称,值为回调函数。

    也就是说 对orders这个关联返回的ActiveQuery,再执行一次$query->andWhere(‘subtotal>100′);

    使用joinWith进行表关联

    我们都知道可以用join on来写多个表之间的关联。先看看yii2中joinWit的声明
    joinWith( $with, $eagerLoading = true, $joinType = ‘LEFT JOIN’ )

    • $with 数据类型为字符串或数组,
      如果为字符串,则为模型中定义的关联的名称(可以为子关联)。
      如果为数组,键为model中以getXXX格式定义的关联,值为对这个关联的进一步的回调操作。
    • $eagerLoading 是否加载在$with中关联的模型的数据。
    • $joinType 联接类型,可用值为:LEFT JOIN、INNER JOIN,默认值为LEFT JOIN

      $orders = Order::find()->joinWith(‘customer’)->orderBy(‘customer.id, order.id’)->all();// 订单表和客户表以Left join的方式关联,查找所有订单,并以客户 ID 和订单 ID 排序

      $orders = Order::find()->innerJoinWith(‘books’)->all(); // 订单表和客户表以Inner join的方式关联,查找所有的订单和书

     // 使用inner join 连接order中的 books关联和customer关联。

     // 并对custmer关联再次进行回调过滤:找出24小时内注册客户包含书籍的订单

     $orders = Order::find()->innerJoinWith([

         'books',

         'customer' => function ($query) {

             $query->where('customer.created_at > ' . (time() - 24 * 3600));

         }

     ])->all();

    $orders = Order::find()->joinWith(‘books.author’)->all();// 使用left join连接 books关联,books关联再用left join 连接 author关联

    在实现上,Yii 先执行满足JOIN查询条件的SQL语句,把结果填充到主模型中, 然后再为每个关联执行一条查询语句, 并填充相应的关联模型。

    $orders = Order::find()->innerJoinWith(‘books’, false)->all();// Order和books关联 inner join ,但不获取books关联对应的数据 

    On条件

    在定义关联的时候还可以指定on条件

    class User extends ActiveRecord{

        public function getBooks(){

            return $this->hasMany(Item::className(), ['owner_id' => 'id'])->onCondition(['category_id' => 1]);

        }

    } 

    在joinWith中使用

    1. //先查询主模型(User)的数据, SELECT user.* FROM user LEFT JOIN item ON item.owner_id=user.id AND category_id=1
    2. // 然后再根据关联条件查询相关模型数据SELECT * FROM item WHERE owner_id IN (…) AND category_id=1
    3. // 这两个在查询的过程中都使用了 on条件。
    4. $users = User::find()->joinWith(‘books’)->all();

     

    如果没有使用join操作,即使用的是with 或者 直接以属性来访问关联。这个时候on条件会作为where 条件。

    $user = User::findOne(10);// SELECT * FROM user WHERE id=10

    总结

      • 首先需要在模型中定义好关联(如getOrders中的Orders为一个关联)
      • 然后在with或者joinWith中使用在模型中定义的关联。
      • 其中在使用关联的时候还可以指定回调方法。
      • 再有就是可以给关联、with、joinWith指定where或者on条件
  • 相关阅读:
    MFC tab页面中获到其它页面的数据
    sqlite数据库中"Select * From XXX能查到数据,但是Select DISTINCT group From xxx Order By group却查不出来
    关闭程序出现崩溃(exe 已触发了一个断点及未加载ucrtbased.pdb)
    springboot 通用Mapper使用
    springBoot 发布war包
    springCloud Zuul网关
    springboot hystrix turbine 聚合监控
    springBoot Feign Hystrix Dashboard
    springBoot Ribbon Hystrix Dashboard
    springBoot Feign Hystrix
  • 原文地址:https://www.cnblogs.com/xlz307/p/5216039.html
Copyright © 2011-2022 走看看