Laravel: Accessing model attributes in the constructor method - laravel

How to use __construct on laravel eloquent and get attributes. I tired:
public function __construct() {
parent::__construct();
dd($this->attributes);
}
My code return null. But on abstract class Model filled all attributes:
public function __construct(array $attributes = [])
{
$this->bootIfNotBooted();
$this->initializeTraits();
$this->syncOriginal();
$this->fill($attributes);
}
It's possible get access to model attributes in the constructor method?

Try with accessors.
https://laravel.com/docs/5.8/eloquent-mutators#accessors-and-mutators
Define it like:
public function getFirstNameAttribute($value)
{
return ucfirst($value);
}
And use $this->first_namesomething like this.

I tested this locally by updating the constructor like this:
public function __construct(array $attributes = []) {
parent::__construct($attributes);
dd($this->getAttributes());
}
However, I've discovered that when fetching the object from the database, its attributes are not filled in the constructor, and therefore it's not possible to access them there.
What you can do is access the attributes after the object has been initialized:
$post = Post::find(1);
dump($post->getAttributes());
Not sure if that helps, but it is what it is.
Maybe Events or Observers can help you with what you need:
https://laravel.com/docs/5.8/eloquent#events
https://laravel.com/docs/5.8/eloquent#observers

It's impossible in constructor, because attributes list is empty.
But you can use Events and boot() method of model to achieve desired result:
class MyModel extends Model{
public static function boot(){
parent::boot();
self::retrieved(function ($model) {# Called after data loaded from db
dd($this->attributes); # now attributes was filled
});
}
}
Read more about another events here:
https://www.itsolutionstuff.com/post/laravel-model-events-tutorialexample.html

Related

Laravel show with relationships

How do you use the show function relationships? i know this works:
public show ($id) {
Model::with('relationship')->find($id);
}
but with the new format
public show(Model $model) {
}
how do you include the relationship?
i've tried
$model->with('relationship')->get();
but it changes the value from an object to an array, what would be the proper way to do this?
Lets lazy eager load that:
public show(Model $model) {
$model->load('relationship');
}
That's not a "new format". That's in fact Route model binding which is a convenient way to work as an API. https://laravel.com/docs/9.x/routing#route-model-binding
When you have a route such as
Route::get('/users/{user}', [UserController::class, 'show']);
Your controller will receive the model already fetched from database.
If you need to use additional relationships you have 2 options (let's assume that user has a profile relationship):
Eager load on controller
public show(User $user) {
$user->load('profile');
return $user;
}
Or eager load in your RouteServiceProvider.php by using explicit binding. https://laravel.com/docs/9.x/routing#explicit-binding
/**
* Define your route model bindings, pattern filters, etc.
*
* #return void
*/
public function boot()
{
Route::bind('user', function ($value) {
return User::with('profile')->findOrFail($value);
});
}
Therefore you will have the user with it's profile in your controller

How to always use withTrashed() for model Binding

In my app, I use soft delete on a lot of object, but I still want to access them in my app, just showing a special message that this item has been deleted and give the opportunity to restore it.
Currently I have to do this for all my route parametters in my RouteServiceProvider:
/**
* Define your route model bindings, pattern filters, etc.
*
* #return void
*/
public function boot()
{
parent::boot();
Route::bind('user', function ($value) {
return User::withTrashed()->find($value);
});
Route::bind('post', function ($value) {
return Post::withTrashed()->find($value);
});
[...]
}
Is there a quicker and better way to add the trashed Object to the model binding ?
Jerodev's answer didn't work for me. The SoftDeletingScope continued to filter out the deleted items. So I just overrode that scope and the SoftDeletes trait:
SoftDeletingWithDeletesScope.php:
namespace App\Models\Scopes;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletingScope;
class SoftDeletingWithDeletesScope extends SoftDeletingScope
{
public function apply(Builder $builder, Model $model)
{
}
}
SoftDeletesWithDeleted.php:
namespace App\Models\Traits;
use Illuminate\Database\Eloquent\SoftDeletes;
use App\Models\Scopes\SoftDeletingWithDeletesScope;
trait SoftDeletesWithDeleted
{
use SoftDeletes;
public static function bootSoftDeletes()
{
static::addGlobalScope(new SoftDeletingWithDeletesScope);
}
}
This effectively just removes the filter while still allowing me to use all the rest of the SoftDeletingScope extensions.
Then in my model I replaced the SoftDeletes trait with my new SoftDeletesWithDeleted trait:
use App\Models\Traits\SoftDeletesWithDeleted;
class MyModel extends Model
{
use SoftDeletesWithDeleted;
For Laravel 5.6 to 7
You can follow this doc https://laravel.com/docs/5.6/scout#soft-deleting. And set the soft_delete option of the config/scout.php configuration file to true.
'soft_delete' => true,
For Laravel 8+
You can follow this doc https://laravel.com/docs/8.x/routing#implicit-soft-deleted-models. And append ->withTrashed() to the route that should accept trashed models:
Ex:
Route::get('/users/{user}', function (User $user) {
return $user->email;
})->withTrashed();
You can add a Global Scope to the models that have to be visible even when trashed.
For example:
class WithTrashedScope implements Scope
{
public function apply(Builder $builder, Model $model)
{
$builder->withTrashed();
}
}
class User extends Model
{
protected static function boot()
{
parent::boot();
static::addGlobalScope(new WithTrashedScope);
}
}
Update:
If you don't want to show the deleted objects you can still manually add ->whereNull('deleted_at') to your query.

Laravel Mutators with Constructor?

Ill have a problem because my mutators never get called when ill use an constructor:
Like this:
function __construct() {
$this->attributes['guid'] = Uuid::generate(4)->string;
}
public function setDateAttribute($date) {
dd($date); // Never gets called
}
Ill already found out, that the mutators would ne be called when ill use an constructor, so i should use:
public function __construct(array $attributes = array()){
parent::__construct($attributes);
$this->attributes['guid'] = Uuid::generate(4)->string;
}
public function setDateAttribute($date) {
dd($date); // now its getting called
}
But so ill get the following error:
array_key_exists() expects parameter 2 to be array, null given
But i dont know where? Can anyone help me out how to create a default value (like a UUID) for a specific column, and use mutators in the same class?
Edit: Thanks Martin Bean for your help, but i am now getting the following error:
Cannot declare class App\Uuid because the name is already in use
I have tried:
Creating a File called "Uuid.php" in /app/ -> /app/Uuid.php
With this content:
<?php namespace App;
use Webpatser\Uuid\Uuid;
trait Uuid
{
public static function bootUuid()
{
static::creating(function ($model) {
$model->uuid = Uuid::generate(4)->string();
});
}
}
Changed my Model to:
<?php namespace App;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
class Task extends Model {
use \App\Uuid;
Thank you very much!
Edit 2:
Ill tried it this way:
class Task extends Model {
protected $table = 'tasks';
protected $fillable = ['..... 'date', 'guid'];
public function setGuidAttribute($first=false){
if($first) $this->attributes['guid'] = Uuid::generate(4)->string;
}
TaskController:
public function store() {
$input = Request::all();
$input['guid'] = true;
Task::create($input);
return redirect('/');
}
Works fine, but when ill use:
public function setDateAttribute(){
$this->attributes['date'] = date('Y-m-d', $date);
}
In Task.php ill get:
Undefined variable: date
EDITED:
based on your comment:
i would like to set a field on first insert
use Uuid; //please reference the correct namespace to Uuid
class User extends Model{
protected $fillable = [
'first_name',
'email',
'guid' //add guid to list of your fillables
]
public function setGuidAttribute($first=false){
if($first) $this->attributes['guid'] = Uuid::generate(4)->string;
}
}
Later:
$user = User::create([
'guid' => true, //setAttribute will handle this
'first_name' => 'Digitlimit',
'email" => my#email.com
]);
dd($user->guid);
NB: Remove the __construct() method from your model
Mutators are called when you try and set a property on the model—they’re invoked via the __get magic method. If you manually assign a property in a method or constructor, then no mutators will ever be called.
Regardless, you should not be creating constructors on Eloquent model classes. This could interfere with how Eloquent models are “booted”.
If you need to set an UUID on a model then I’d suggest using a trait that has its own boot method:
namespace App;
trait Uuid
{
public static function bootUuid()
{
static::creating(function ($model) {
$model->uuid = \Vendor\Uuid::generate(4)->string();
});
}
}
You apply the trait to your model…
class SomeModel extends Model
{
use \App\Uuid;
}
…and now each time a model is created, a UUID will be generated and stored in the database with your model.

Laravel 4: Reference controller object inside filter

I have a controller in Laravel 4, with a custom variable declared within it.
class SampleController extends BaseController{
public $customVariable;
}
Two questions: Is there any way I can call within a route filter:
The controller object where the filter is running at.
The custom variable from that specific controller ($customVariable).
Thanks in advance!
as per this post:
http://forums.laravel.io/viewtopic.php?pid=47380#p47380
You can only pass parameters to filters as strings.
//routes.php
Route::get('/', ['before' => 'auth.level:1', function()
{
return View::make('hello');
}]);
and
//filters.php
Route::filter('auth.level', function($level)
{
//$level is 1
});
In controllers, it would look more like this
public function __construct(){
$this->filter('before', 'someFilter:param1,param2');
}
EDIT:
Should this not suffice to your needs, you can allways define the filter inside the controller's constructor. If you need access to the current controller ($this) and it's custom fields and you have many different classes you want to have that in, you can put the filter in BaseController's constructor and extend it in all classes you need.
class SomeFancyController extends BaseController {
protected $customVariable
/**
* Instantiate a new SomeFancyController instance.
*/
public function __construct()
{
$ctrl = $this;
$this->beforeFilter(function() use ($ctrl)
{
//
// do something with $ctrl
// do something with $ctrl->customVariable;
});
}
}
EDIT 2 :
As per your new question I realised the above example had a small error - as I forgot the closure has local scope. So it's correct now I guess.
If you declare it as static in your controller, you can call it statically from outside the controller
Controller:
class SampleController extends BaseController
{
public static $customVariable = 'test';
}
Outside your controller
echo SampleController::$customVariable
use:
public function __construct()
{
$this->beforeFilter('auth', ['controller' => $this]);
}

Codeigniter: Set 'global variable'

If I want to set a variable that my whole controller can access, how do I do it?
Right now, in every function I am setting
$id = $this->session->userdata('id');
I'd like to be able to access $id from any function w/o defining it for each controller. :)
If there's a better way, I'm all ears! I'm a noob!
To elaborate on Koo5's response, you'll want to do something like this:
class yourController extends Controller {
// this is a property, accessible from any function in this class
public $id = null;
// this is the constructor (mentioned by Koo5)
function __construct() {
// this is how you reference $id within the class
$this->id = $this->session->userdata('id');
}
function getID() {
// returning the $id property
return $this->id;
}
}
See the manual for more information on PHP properties and constructors. Hope that helps!
If with global you mean the scope of a single controller, the above answers are correct. If you want to access variables from multiple controllers, I recommend defining a BaseController class and then extending your normal controllers to inherit from that BaseController:
class yourController extends BaseController {}
I use this all the time for both global variables and methods.
Define that very line in a constructor only
You can use this method too inside your controller
function __construct() {
// this is how you reference $id within the class
DEFINE("userId",$this->session->userdata('id'));
}
And Call It As :
function getID() {
// returning the $id property
return userId;
}

Resources