passing arguments to Model constructor - php

How can i pass my arguments to the model constructor,i have the following code in my controller
public static function inbox(){
$user=Sentry::getUser();
$results=new Message($user->id);
$inbox=$results->inbox();
return $inbox;
}
And in my modal i am extending the parent modal constructor as follows
class Message extends \Eloquent {
private $user_id;
public function __construct($attributes = array()) {
parent::__construct($attributes); // Eloquent
}}
now i want to pass the $user_id to the Message modal constructor,how do i achieve this
when inserting data to the db using the Eloquent create nothing is being inserted into the database here is the code
Message::create(
array(
'msg_id'=>$result->id,
'subj'=>Input::get('subj'),
'content'=>Input::get('content'),
'sender_id'=>Input::get('sender_id'),
'receivers_id'=>$user->id,
'file'=>Input::get('file')
)
);
its like i have overidden built-in constructor of the Model class

You can use #PeterPopelyshko's approach and override the constructor or just use what Eloquent offers. Mass Assignment.
You can pass in attributes through your constructor by using an associative array:
$results = new Message(array(
'user_id' => $user->id
));
Just make sure to define all the properties, you want to be fillable in the $fillable array in your model:
class Message extends \Eloquent {
protected $fillable = array('user_id');
}
Note that you don't need the private $user_id if this is a database field. Laravel handles them in it's own $attributes array.

class Message extends \Eloquent {
private $user_id;
public function __construct($user_id, $attributes = array()) {
$this->user_id = $user_id;
parent::__construct($attributes); // Eloquent
}}
and then you can use it in your controller
$results=new Message($user->id);
and you can get access to $user_id within model like this $this->user_id

Related

Laravel object value with Blade statement does not work if model has accessor

i have an Laravel object model with accessor:
class NutritionalPlanRow extends Model
{
use HasFactory;
private $nomeAlimento;
public function __construct($aliment = null,
array $attributes = array()) {
parent::__construct($attributes);
if($aliment){
$this->aliment()->associate($aliment);
$this->nomeAlimento = $aliment->nome;
}
}
public function aliment()
{
return $this->belongsTo('App\Models\Aliment');
}
protected $guarded = [];
public function getNomeAlimentoAttribute()
{
return $this->nomeAlimento;
}
}
and i want to print the nomeAlimento value in a Blade page with Blade statement, for example:
.
.
<tbody>
#foreach( $plan->nutritionalPlanRows as $planRow )
<tr>
<td>
{{ $planRow->nomeAlimento}}
</td>
.
.
but the value inside the table cell is not printed, as if $planRow->foodName is null. In reality it is not empty, in fact if I print {{$planRow}} the structure of the object is complete, and all the attributes are set.
I noticed that if in the model I remove the accessor (getNomeAlimentoAttribute()), then the value in the blade page is correctly printed.
Why?
Thanks.
There are a few things that need attention:
First: Why do you need a constructor? You can define a calculated attribute without the constructor
use App\Models\Aliment;
class NutritionalPlanRow extends Model
{
use HasFactory;
public function aliment()
{
return $this->belongsTo(Aliment::class);
}
protected $guarded = [];
public function getNomeAlimentoAttribute()
{
return optional($this->ailment)->nome;
}
}
Second: It seems like a code smell when using constructor in Eloquent Model class to set relations. Ideally relations should be set/associated from within Controller.
Third: I feel declaring $nomeAlimento as private property on the class is not required. In Laravel calculated properties/attributes can be provided with accessors.
Update:
class Patient extends Model
{
use HasFactory;
protected $dates = ['day_born'];
protected $guarded = [];
public function getYearsAttribute(){
Log::info('patient all data '.$this); //Print correct all data
Log::info('Day'.$this->day_born); //print empty
return Carbon::parse($this->day_born)->diffForHumans(now());
}
}
Read https://carbon.nesbot.com/docs/ for more goodies.

Laravel 5.6 database operations in model

I have a controller called UserController, in that controller i am inserting a row of data to table "user" like this
$user = new UsersModel();
$user->first_name = $request->input('firstName');
$user->last_name = $request->input('lastName');
$user->about = $request->input('userAbout');
$user->join_date = date('Y-m-d');
$user->save();
My Question is, can i write this in my model called UsersModel???
Something Like,
( The insertData($data) is called from controller class.)
class UsersModel extends Model
{
protected $fillable = ['id','first_name','last_name','about','image','join_date','created_at','updated_at'];
protected $table = 'users';
public function insertData($data) {
// nb: $data contains values of fileds
// insert operation
//also return some values
}
}
You don't need to define your own function when you can already do it through Eloquent by simply calling the static method create magically:
$ref = UsersModel::create([
'col' => 'val'
]);
where $ref contains the information about the created data.
No need to reinvent the wheel in this instance.
However, your own custom method is possible too, make sure your function is defined as static to allow you to use without an object reference.
Yes you can
you need to call the function from the controller like this
$data = ['YOUR ARRAY'];
$this->usersModel = new UsersModel();
$this->usersModel->insertData($data);
You can also do with static function
In Model
public static function insertData($data) {
In Controller
UsersModel::insertData($data);
Insert function
UsersModel::insert($data);

Return class variable of a Laravel Model in JSON

I have the following model class
class MyModel extends Model {
public $some_variable; // I don't want to store this in the database
protected $fillable = ['column1', 'column2'];
In the controller:
$model = MyModel::find(2);
$model->some_variable = "some value"; // Dynamically calculated each time
return response()->json($model);
The response contains all the columns from MyModel but does not contain $some_variable. Why could this be happening? Are class variables transient by default?
Model's data is internally kept in $attributes array, so you may want to put it there prior converting your data to JSON:
...
$model->some_variable = ...;
return response()->json($model);
Because you have defined $some_variable on the Model, it will not show up in the array/json output. The array/json output only includes the table data (stored in the $attributes property) and the loaded relationship data.
If you want this field to show up, you can override the toArray() method on the model, or you could create an accessor method and add that to the $appends property.
Override toArray():
class MyModel extends Model
{
public $some_variable;
public function toArray()
{
$data = parent::toArray();
$data['some_variable'] = $this->some_variable;
return $data;
}
}
Use an accessor and $appends:
class MyModel extends Model
{
public $some_variable;
protected $appends = ['some_variable'];
public function getSomeVariableAttribute()
{
return $this->some_variable;
}
}
You can read about accessors here. You can read about appending data to json here.

Laravel eloquent issue with constructor

I have a model which contains many methods.
class UserModel extends Eloquent{
private $active;
function __construct() {
$this->active = Config::get('app.ActiveFlag');
}
protected $table = 'User';
protected $fillable = array('usr_ID', 'username');
public function method1(){
//use $active here
}
public function method2(){
//use $active here
}
}
Controller:
$user = new UserModel($inputall);
$user->save();
Without constructor, it works fine. However, with constructor it doesn't save the user (the query which is generated doesn't have any fill attributes or values). The query is as follows:
insert into User() values();
Any inputs please?
Well yes, that's because you override the Eloquent constructor which is responsible to fill the model with values when an array is passed. You have to pass them along to the parent with parent::__construct():
public function __construct(array $attributes = array()){
parent::__construct($attributes);
$this->active = Config::get('app.ActiveFlag');
}
Your model's constructor doesn't accept any parameters - empty (), and you are creating new instance of UserModel in your controller adding $inputall as a parameter.
Try to refactor your contructor according to this:
class UserModel extends Eloquent {
public function __construct($attributes = array()) {
parent::__construct($attributes);
// Your additional code here
}
}
(Answer based on other Eloquent contructor question)

Updating timestamps on attaching/detaching Eloquent relations

I'm using Laravel 4, and have 2 models:
class Asset extends \Eloquent {
public function products() {
return $this->belongsToMany('Product');
}
}
class Product extends \Eloquent {
public function assets() {
return $this->belongsToMany('Asset');
}
}
Product has the standard timestamps on it (created_at, updated_at) and I'd like to update the updated_at field of the Product when I attach/detach an Asset.
I tried this on the Asset model:
class Asset extends \Eloquent {
public function products() {
return $this->belongsToMany('Product')->withTimestamps();
}
}
...but that did nothing at all (apparently). Edit: apparently this is for updating timestamps on the pivot table, not for updating them on the relation's own table (ie. updates assets_products.updated_at, not products.updated_at).
I then tried this on the Asset model:
class Asset extends \Eloquent {
protected $touches = [ 'products' ];
public function products() {
return $this->belongsToMany('Product');
}
}
...which works, but then breaks my seed which calls Asset::create([ ... ]); because apparently Laravel tries to call ->touchOwners() on the relation without checking if it's null:
PHP Fatal error: Call to undefined method Illuminate\Database\Eloquent\Collection::touchOwners() in /projectdir/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php on line 1583
The code I'm using to add/remove Assets is this:
Product::find( $validId )->assets()->attach( $anotherValidId );
Product::find( $validId )->assets()->detach( $anotherValidId );
Where am I going wrong?
You can do it manually using touch method:
$product = Product::find($validId);
$product->assets()->attach($anotherValidId);
$product->touch();
But if you don't want to do it manually each time you can simplify this creating method in your Product model this way:
public function attachAsset($id)
{
$this->assets()->attach($id);
$this->touch();
}
And now you can use it this way:
Product::find($validId)->attachAsset($anotherValidId);
The same you can of course do for detach action.
And I noticed you have one relation belongsToMany and the other hasMany - it should be rather belongsToMany in both because it's many to many relationship
EDIT
If you would like to use it in many models, you could create trait or create another base class that extends Eloquent with the following method:
public function attach($id, $relationship = null)
{
$relationship = $relationship ?: $this->relationship;
$this->{$relationship}()->attach($id);
$this->touch();
}
Now, if you need this functionality you just need to extend from another base class (or use trait), and now you can add to your Product class one extra property:
private $relationship = 'assets';
Now you could use:
Product::find($validId)->attach($anotherValidId);
or
Product::find($validId)->attach($anotherValidId, 'assets');
if you need to attach data with updating updated_at field. The same of course you need to repeat for detaching.
From the code source, you need to set $touch to false when creating a new instance of the related model:
Asset::create(array(),array(),false);
or use:
$asset = new Asset;
// ...
$asset->setTouchedRelations([]);
$asset->save();
Solution:
Create a BaseModel that extends Eloquent, making a simple adjustment to the create method:
BaseModel.php:
class BaseModel extends Eloquent {
/**
* Save a new model and return the instance, passing along the
* $options array to specify the behavior of 'timestamps' and 'touch'
*
* #param array $attributes
* #param array $options
* #return static
*/
public static function create(array $attributes, array $options = array())
{
$model = new static($attributes);
$model->save($options);
return $model;
}
}
Have your Asset and Product models (and others, if desired) extend BaseModel rather than Eloquent, and set the $touches attribute:
Asset.php (and other models):
class Asset extends BaseModel {
protected $touches = [ 'products' ];
...
In your seeders, set the 2nd parameter of create to an array which specifies 'touch' as false:
Asset::create([...],['touch' => false])
Explanation:
Eloquent's save() method accepts an (optional) array of options, in which you can specify two flags: 'timestamps' and 'touch'. If touch is set to false, then Eloquent will do no touching of related models, regardless of any $touches attributes you've specified on your models. This is all built-in behavior for Eloquent's save() method.
The problem is that Eloquent's create() method doesn't accept any options to pass along to save(). By extending Eloquent (with a BaseModel) to accept the $options array as the 2nd attribute, and pass it along to save(), you can now use those two options when you call create() on all your models which extend BaseModel.
Note that the $options array is optional, so doing this won't break any other calls to create() you might have in your code.

Categories