Conditionally assign Laravel's controller values on Construct method - laravel

I work with a Laravel porject that uses an array of Model attributes to show it on the view. Unlike most Laravel prokects where do you send a set of data to the view and you choose how to show it on our project that's defined on a Controller's variable.
This variable ($showFields) expects an array of Model's atributes and the values are shown on the view.
The thing is that I need to adapt the shown atributes based on one atrubute's id (vendor_id). My idea is, based on the vendor_id assign one array of attributes or another.
I've been trying with the Controller's constructor method but it's not working as when it's called seems like it does not have the values yet. Is this even possible?
This is the controller's code:
protected $showFields = []; // This is the variable that tells the view what attributes to show
protected $vendorInformation = [...];
protected $noVendorInformation = [...];
public function __construct(QuoteService $quoteService)
{
parent::__construct();
$this->quoteService = $quoteService;
if($this->vendor_id === xx){
array_push($this->showFields, $this->noVendorInformation);
} else {
array_push($this->showFields, $this->vendorInformation);
}
}
I'm working with Laravel 5.7

Related

OctoberCMS - How to set dynamin value for repeater in filterFields?

In my OctoberCMS application, I want to change a repeater value depend on another field value change. But it's not working in the filterFields model method or formExtendRefreshFields controller method.
I am trying like bellow,
public function filterFields($fields, $context = null)
{
$fields->item_details->value = [my prepared array value];
}
all other property like label, comments working fine in filterFields as well.

why is $this->field not always returning a value inside laravel custom attribute model

I have a custom attribute inside my model.
if I try to get the value of i.e $this->id it works but if I add a new column to that model and call it $this->new_column it returns empty even though it has a value. So in order to retrieve it, I have to then initialise the model again inside the custom attribute.
$model = MyModel::find($this->id);
just so I can access $model->new_column
public function getCustomColumnDataAttribute(){
Log::info($this->id); //returns the value
Log::info($this->new_column); //doesn't return the value
$model = MyModel::find($this->id);
Log::info($model->new_column); //returns the value
}
I find it a waste to always initialise the model again just so I can access the values. Or am I missing something here with how $this works inside the model function?

How do i get data once in model and use the same data in multiple views passing through controller in laravel 8.x

For example, I want data from 3 models to MyModel4.
$model1data = MyModel1::getList();
$model2data = MyModel2::getList();
$model3data = MyModel3::getList();
Purpose of getting these data is to prepare my create view, edit view and show view based on above models data.
Now the issue I am facing is I can get the data using MyModel1::getList(); and pass it to my controller's create method. The same data I want for my controller's show and edit method. And for that again I have to get data using MyModel1::getList();. I want to optimize my code for less interaction with database/reduce redundant processing.
So is there any shorter method by which I can write it in my controller or in my model to get these data once and use it multiple times without getting data repeatedly from another model?
I have tried defining the separate method in the model but unable to access using a variable.
public static function getRequiredData()
{
$model1data = MyModel1::getList();
$model2data = MyModel2::getList();
$model3data = MyModel3::getList();
}
public static function prepareCreate()
{
return (object)array("model1" => PublishPlan::getRequiredData().$model1data, "viewModes" => ViewMode::getList(), "planSchemes" => PlanSchemes::getList());
}
I am getting an error while accessing $model1data in the above method(prepareCreate).
I think you should use a constructor for your issue.
public function __construct()
{
$model1data = MyModel1::getList();
$model2data = MyModel2::getList();
$model3data = MyModel3::getList();
}
with the constructor, you can access the lists in all methods of the same controller.
but if you want to access the same list in all controllers, you should define a static variable in Model1, Model2, and Model3.

Yii2: How to set default attribute values in ActiveRecord?

This may seem like a trivial question, however all of the obvious solutions that I can think of have their own flaws.
What we want is to be able to set any default ActiveRecord attribute value for new records only, in a way that makes it readable before and during validation and does not interfere with derived classes used for search.
The default values need to be set and ready as soon as we instantiate the class, so that (new MyModel)->attr returns the default attr value.
Here are some of the possibilities and the problems they have:
A) In MyModel override the init() method and assign default value when isNewRecord is true like so:
public function init() {
if ($this->isNewRecord) {
$this->attr = 'defaultValue';
}
parent::init();
}
Problem: Search. Unless we explicitly unset our default attribute in MySearchModel (very error-prone because it is too easy to forget), this will also set the value before calling search() in the derived MySearchModel class and interfere with searching (the attr attribute will already be set so search will be returning incorrect results). In Yii1.1 this was resolved by calling unsetAttributes() before calling search(), however no such method exists in Yii2.
B) In MyModel override the beforeSave() method like so:
public function beforeSave($insert) {
if ($insert) {
$this->attr = 'defaultValue';
}
return parent::beforeSave();
}
Problem: Attribute is not set in unsaved records. (new MyModel)->attr is null. Worse yet, even other validation rules that rely on this value will not be able to access it, because beforeSave() is called after validation.
C) To ensure the value is available during validation we can instead override the beforeValidate() method and set the default values there like so:
public function beforeValidate() {
if ($this->isNewRecord) {
$this->attr = 'defaultValue';
}
return parent::beforeValidate();
}
Problem: Attribute is still not set in unsaved (unvalidated) records. We need to at least call $model->validate() if we want to get the default value.
D) Use DefaultValidator in rules() to set a default attribute value during validation like so:
public function rules() {
return [
[
'attr', 'default',
'value' => 'defaultValue',
'on' => 'insert', // instantiate model with this scenario
],
// ...
];
}
Problem: Same as B) and C). Value is not set until we actually save or validate the record.
So what is the right way to set default attribute values? Is there any other way without the outlined problems?
There's two ways to do this.
$model => new Model();
Now $model has all the default attributes from the database table.
Or in your rules you can use:
[['field_name'], 'default', 'value'=> $defaultValue],
Now $model will always be created with the default values you specified.
You can see a full list of core validators here http://www.yiiframework.com/doc-2.0/guide-tutorial-core-validators.html
This is a hangup with Yii's bloated multi-purpose ActiveRecords
In my humble opinion the form models, active records, and search models would be better off split into separate classes/subclasses
Why not split your search models and form models?
abstract class Creature extends ActiveRecord {
...
}
class CreatureForm extends Creature {
public function init() {
parent::init();
if ($this->isNewRecord) {
$this->number_of_legs = 4;
}
}
}
class CreatureSearch extends Creature {
public function search() {
...
}
}
The benefits of this approach are
You can easily cater for different validation, set up and display cases without resorting to a bunch of ifs and switches
You can still keep common code in the parent class to avoid repetition
You can make changes to each subclass without worrying about how it will affect the other
The individual classes don't need to know about the existence of any of their siblings/children to function correctly
In fact, in our most recent project, we are using search models that don't extend from the related ActiveRecord at all
I know it is answered but I will add my approach.
I have Application and ApplicationSearch models. In Application model I add init with a check of the current instance. If its ApplicationSearch I skip initializations.
public function init()
{
if(!$this instanceof ApplicationSearch)
{
$this->id = hash('sha256', 123);
}
parent::init();
}
also as #mae commented below you can check for existence of search method in current instance, assuming you didn't add any method with name search to the non-search base model so the code becomes:
public function init()
{
// no search method is available in Gii generated Non search class
if(!method_exists($this,'search'))
{
$this->id = hash('sha256', 123);
}
parent::init();
}
I've read your question several times and I think there are some contradictions.
You want the defaults to be readable before and during validation and then you try init() or beforeSave(). So, assuming you just want to set the default values in the model so they can be present during the part of the life cycle as long as possible and not interfere with the derived classes, simply set them after initialising the object.
You can prepare separate method where all defaults are set and call it explicitly.
$model = new Model;
$model->setDefaultValues();
Or you can create static method to create model with all default values set and return the instance of it.
$model = Model::createNew();
Or you can pass default values to constructor.
$model = new Model([
'attribute1' => 'value1',
'attribute2' => 'value2',
]);
This is not much different from setting the attributes directly.
$model = new Model;
$model->attribute1 = 'value1';
$model->attribute2 = 'value2';
Everything depends on how much transparent would you like your model be to your controller.
This way attributes are set for the whole life cycle except the direct initialisation and it's not interfering with derived search model.
Just override __construct() method in your model like this:
class MyModel extends \yii\db\ActiveRecord {
function __construct(array $config = [])
{
parent::__construct($config);
$this->attr = 'defaultValue';
}
...
}
If you want to load default value from database you can put this code in your model
public function init()
{
parent::init();
if(!method_exists($this,'search')) //for checking this code is on model search or not
{
$this->loadDefaultValues();
}
}
You can prepare separate method where all defaults are set and call it explicitly.
$model = new Model;
if($model->isNewRecord())
$model->setDefaultValues();

Yii2: ActiveRecord How to unload / unset a model of all(some) attributes?

Yii2 ActiveRecord has a method to automatically load a form data into a model using load() which is very good as it safely loads the model with data, However I am not able find a equivalent method to unload the model of all the attributes.
i.e. Is there a method to unset all attributes of a model in Yii2, like the unSetAttributes() method in Yii 1.x ?
Currently the only way to do this seems to be either
$model->setAttributes(['attribute1'=>NULL,'attribute2' => NULL ... ]);
or
foreach ($model->attributes as $attribute) {
$model->$attribute = NULL;
}
Edit: To clarify in response to Samuel Liew's answer, while at this point I only wanted to unset all attributes which I could do by reiniting the model, I would also like to control which attributes are getting reset, which unSetAttributes provided
You could simply create a new instance of the model.
$model = new MyModel;
Or as you can see, unsetAttributes in Yii 1 is like this, you could simply implement it in your base model:
public function unsetAttributes($names=null)
{
if($names===null)
$names=$this->attributeNames();
foreach($names as $name)
$this->$name=null;
}

Resources