1

I've read all the Yii2 Framework documentation but it's confusing when trying to implement it.

I have a view of Customer where it shows all the fields in customer table, including the address_id field in the address table.

I want to implement a join query using MySQL in the Yii2 Framework but the generated code is the following:

CustomerSearch in the models:

class CustomerSearch extends Customer{
/**
 * {@inheritdoc}
 */
public function rules()
{
    return [
        [['customer_id', 'store_id', 'address_id', 'active'], 'integer'],
        [['first_name', 'last_name', 'email', 'create_date', 'last_update'], 'safe'],
    ];
}

/**
 * {@inheritdoc}
 */
public function scenarios()
{
    // bypass scenarios() implementation in the parent class
    return Model::scenarios();
}

/**
 * Creates data provider instance with search query applied
 *
 * @param array $params
 *
 * @return ActiveDataProvider
 */
public function search($params)
{
    $query = Customer::find();

    // add conditions that should always apply here

    $dataProvider = new ActiveDataProvider([
        'query' => $query,
    ]);

    $this->load($params);

    if (!$this->validate()) {
        // uncomment the following line if you do not want to return any records when validation fails
        // $query->where('0=1');
        return $dataProvider;
    }

    // grid filtering conditions
    $query->andFilterWhere([
        'customer_id' => $this->customer_id,
        'store_id' => $this->store_id,
        'address_id' => $this->address_id,
        'active' => $this->active,
        'create_date' => $this->create_date,
        'last_update' => $this->last_update,
    ]);

    $query->andFilterWhere(['like', 'first_name', $this->first_name])
        ->andFilterWhere(['like', 'last_name', $this->last_name])
        ->andFilterWhere(['like', 'email', $this->email]);

    return $dataProvider;
}

Customer class:

class Customer extends \yii\db\ActiveRecord{
/**
 * {@inheritdoc}
 */
public static function tableName()
{
    return 'customer';
}

/**
 * {@inheritdoc}
 */
public function rules()
{
    return [
        [['store_id', 'first_name', 'last_name', 'address_id'], 'required'],
        [['store_id', 'address_id', 'active'], 'integer'],
        [['create_date', 'last_update'], 'safe'],
        [['first_name', 'last_name'], 'string', 'max' => 45],
        [['email'], 'string', 'max' => 50],
    ];
}

/**
 * {@inheritdoc}
 */
public function attributeLabels()
{
    return [
        'customer_id' => 'Customer ID',
        'store_id' => 'Store ID',
        'first_name' => 'First Name',
        'last_name' => 'Last Name',
        'email' => 'Email',
        'address_id' => 'Address ID',
        'active' => 'Active',
        'create_date' => 'Create Date',
        'last_update' => 'Last Update',
    ];
}
}
4
  • What are you trying to join onto? You must have another table and model and define relations. Did you generate this code with Gii Model/CRUD generators? Commented Jun 7, 2018 at 15:14
  • the table Customer and the table Address using the common field 'address_id' Commented Jun 7, 2018 at 15:16
  • Ah yes, I missed that sorry. Have you used Gii Model/CRUD generators? Commented Jun 7, 2018 at 15:20
  • Yes, the CRUD generator Commented Jun 7, 2018 at 15:30

1 Answer 1

2

You need to declare some relations in your ActiveRecord models...

See here in the official docs for Working with Relational Data

If you are storing the address_id in your customer table then you will be tied to each customer having 1 single address (i.e. a one-to-one relationship), which is a rather bad design. Or you could use a junction table. You are better off storing the customer_id in each address record and defining a one-to-many relation, enabling each customer to store multiple addresses (more like in real life, i.e. for home, work address etc).

For example, in your Customer model, you would define a has-many relation for customer addresses:

use app\models\Address;

class Customer extends \yii\db\ActiveRecord
{

    /**
     * {@inheritdoc}
     */
    public static function tableName()
    {
        return 'customer';
    }

    /**
     * {@inheritdoc}
     */
    public function rules()
    {
        return [
            [['store_id', 'first_name', 'last_name', 'primary_address_id'], 'required'],
            [['store_id', 'primary_address_id', 'active'], 'integer'],
            [['create_date', 'last_update'], 'safe'],
            [['first_name', 'last_name'], 'string', 'max' => 45],
            [['email'], 'string', 'max' => 50],
        ];
    }

    /**
     * {@inheritdoc}
     */
    public function attributeLabels()
    {
        return [
            'customer_id' => 'Customer ID',
            'store_id' => 'Store ID',
            'first_name' => 'First Name',
            'last_name' => 'Last Name',
            'email' => 'Email',
            'primary_address_id' => 'Primary Address ID',
            'active' => 'Active',
            'create_date' => 'Create Date',
            'last_update' => 'Last Update',
        ];
    }

    /**
     * @return \yii\db\ActiveQuery
     */
    public function getAddresses()
    {
        return $this->hasMany(Address::className(), ['customer_id' => 'id']);
    }

}

And in your Address model you would have the inverse has-one relation defined:

    /**
     * @return \yii\db\ActiveQuery
     */
    public function getCustomer()
    {
        return $this->hasOne(Customer::className(), ['id' => 'customer_id']);
    }

You can then Access relational data from model instances via defined relation names, for example:

// SELECT * FROM `customer` WHERE `id` = 123
$customer = Customer::findOne(123);

// SELECT * FROM `address` WHERE `customer_id` = 123
// $addresses is an array of Address objects
$addresses = $customer->addresses;

Also note, if you define proper primary/foreign keys in your schema, the Gii Model/CRUD generators will automatically create the boilerplate relations code in your models and CRUD files.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.