Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Yii2 activerecord custom attribute

I'm trying to use a custom attribute in a model class that extends db\activerecord.

I've tried declaring public $categories = [] and then either assigning values to it directly via $model->categories = [1,2,3] or using a setter method in my model class public function setCategories($ids) {... and then again assigning via $model->categories = [1,2,3].

I've also tried updating the attribute with $model->setAttribute('categories', [1,2,3]).

In all cases $model->categories is not populated.

My end goal is to assign categories to a model and then update the db relation/tables using afterSave() and beforeSave()

Can this be done or should I extend my model class from db\model? If I do, what functionality will I lose?

Edit I might have misstated my problem.

I've got a form where a user can select categories for a specific model (ie "Product"). All categories are pre-existing and products are assigned to categories via a junction table product_category('product_id', 'category_id') with a one-to-many relationship (one product has many categories).

Now in the controller that's handling the view, I'm receiving a list of category id's and I want to assign them to an attribute so that I can process them i.e. delete or add (via link()) entries in the product_category table for the specific product.

like image 488
jimmy Avatar asked May 13 '15 08:05

jimmy


Video Answer


2 Answers

class ClassName extends \yii\db\ActiveRecord
{

   public $addition;   //what attribute you want

    /* your code */

   public function fields()
   {
        $fields = parent::fields();
        $fields[] = 'addition';      //the value must be the same as the new attribute

        /* your code */

        $this->addition = 'done'
        return $fields
   }
}
like image 164
user2866047 Avatar answered Oct 17 '22 08:10

user2866047


It looks to me like you are manually trying to do relations between tables? Why not use the built in functionality and have it taken care of for you?

For this (many-to-many) you need a link activerecord or table that indicates which models link to which categories (I'm assuming here that you have an ActiveRecord that is called ModelCategory and has a model_id and a category_id:

public function getProductCategories() 
{ 
   return $this->hasMany(ProductCategory::className(), ['product_id' => 'id']);
}

public function getCategories()
{
    return $this->hasMany(Category::className(), ['id' => 'category_id'])
        ->via('productCategories');
}

(You can also use viaTable() instead of via() and avoid an extra method, that is your choice.)

This means that you can access them like so:

$product->categories  

(always use the magic functionality for relations, it's the __get() function that actually does the database query).

For assigning relations there is no automatic method. Yii has some assisting functions for this though:

$category = new Category();
// Assign attributes
$category->save();

$product = new Product();
// Assign attributes
$product->save();

$product->link('categories', $category);

Check out the link-function for more details. Obviously there are other ways as well, but it depends on your needs.

As per your extra information:

public function actionAssignCategories($product, $categories) 
{
   $product = Product::findOne($product);

   $existingCategories = \yii\base\ArrayHelper::getColumn($product->categories, 'category_id');
   $removeCategories = array_diff($existingCategories, $categories); 
   $addCategories = array_diff($categories, $existingCategories);             

   foreach ($addCategories as $category) { 
      $category = Category::findOne($category);
      $product->link('categories', $category);
   }

   foreach ($removeCategories as $category) { 
      $category = Category::findOne($category);
      $product->unlink('categories', $category);
   }
}

Untested, but that should give you an idea on how to tackle this.

like image 32
Blizz Avatar answered Oct 17 '22 10:10

Blizz