Laravel Eloquent (A Model attribute won't show) - laravel

I have a Stat Class :
class Stat extends Model
{
public static function getByKey($key)
{
return Stat::where("key", "=", $key)->first();
}
public function getDates()
{
return json_decode($this->content);
}
}
In my database the Table stats contains only two columns (key, content)
When I execute this function in my view an error occurs
{!! \App\Stat::getByKey("ORDERS-DATA")->getDates() !!}
and this makes this error :
Exception message: Undefined property: App\Stat::$content
So I added these two lines to the getDates() to debug the error :
public function getDates()
{
dd($this->content);//This shows the same error
dd($this->attributes["content"]);//This works fine and show me what I want
return json_decode($this->content);
}
then I executed dd($this) and it shows this :
Stat {#486
#connection: "mysql"
#table: "stats"
#primaryKey: "id"
#keyType: "int"
+incrementing: true
#with: []
#withCount: []
#perPage: 15
+exists: true
+wasRecentlyCreated: false
#attributes: array:5 [
"id" => 8
"key" => "ORDERS-DATA"
"content" => """
[
{
\t"day": "2019-04-01",
\t"count": "5"
}
]
"""
"created_at" => "2019-04-01 14:09:00"
"updated_at" => "2019-04-01 14:11:02"
]
#original: array:5 [
"id" => 8
"key" => "ORDERS-DATA"
"content" => """
[
{
\t"day": "2019-04-01",
\t"count": "5"
}
]
"""
"created_at" => "2019-04-01 14:09:00"
"updated_at" => "2019-04-01 14:11:02"
]
#changes: []
#casts: []
#dates: []
#dateFormat: null
#appends: []
#dispatchesEvents: []
#observables: []
#relations: []
#touches: []
+timestamps: true
#hidden: []
#visible: []
#fillable: []
#guarded: array:1 [
0 => "*"
]
}
And What I want is to get my content by $this->content, and I don't understand why it doesn't works ??
Please Help

The error is occurring because I have overrided one of the Laravel functions.
the name getDates OR getByKey OR getCounts is used by Laravel.
This is so weird this error happened to me two times in the same
day, Today's morning I created a function called getAttributes() and
showed me a total diferrent error. Why the community don't create a
list of functions names that we are not allowed to use, or use more
unique names ???

Related

laravel relationship eagerload mailable

I try to pass relationship datas to a mailable, but datas are not in the Mailable build.
The Controller
public function send()
{
$domaines = $this->domaineRepository::domaineNewsletter("2022-05-23", "2022-05-30");
$abonnes = Newsletter::where('name', 'MyNewsletter')->first()->users;
foreach($abonnes as $abonne) {
Mail::to($abonne->email)
->queue(
(new PortailNewsletter("title", "Some text", "2022-05-23", "2022-05-30", $domaines))
->onQueue('portailNewsletter')
);
}
}
The Mailable
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use App\Repositories\Portail\DomaineRepository;
class PortailNewsletter extends Mailable
{
use Queueable, SerializesModels;
public $title;
public $subtitle;
public $date_begin;
public $date_end;
public $domaines;
/**
* The name of the theme that should be used when formatting the message.
*
* #var string|null
*/
public $theme = "portailNewsletter";
/**
* Create a new message instance.
*
* #return void
*/
public function __construct($title, $subtitle, $date_begin, $date_end, $domaines)
{
$this->title= $title;
$this->subtitle= $subtitle;
$this->date_begin= $date_begin;
$this->date_end= $date_end;
$this->domaines = $domaines;
}
/**
* Build the message.
*
* #return $this
*/
public function build()
{
return $this->from([
'address' => 'john.doe#mail.com',
'name' => 'John',
])
->subject($title)
->markdown('portail.mail.newsletter');
}
}
The repository
public static function domaineNewsletter($date_begin, $date_end)
{
$callback = function ($query) use ($date_begin, $date_end) {
$query->where('date_publication', '<=', $date_end)
->where('date_publication', '>=', $date_begin)
->orderBy('date_publication', 'desc');
};
return Domaine::query()->whereHas('actualite', $callback)
->with(['actualite' => $callback])
->get()
;
}
For the domaines datas :
When i do a dd() in the repository, it's OK, i have the expected datas with relationship and eagerload.
When i do a dd() in the send() function of the Controller, it's OK, i have the expected datas with relationship and eagerload.
When i do a dd() in the __construct() function of the Mailable, it's OK, i have the expected datas with relationship and eagerload.
Illuminate\Database\Eloquent\Collection {#705
#items: array:1 [
0 => App\Models\Portail\Domaine {#703
#connection: "mysql_laravel"
#table: "domaines"
#primaryKey: "id"
#keyType: "int"
+incrementing: true
#with: []
#withCount: []
+preventsLazyLoading: false
#perPage: 15
+exists: true
+wasRecentlyCreated: false
#escapeWhenCastingToString: false
#attributes: array:5 [
"id" => 1
"libelle_domaine" => "Domaine1"
"created_at" => null
"deleted_at" => null
"updated_at" => null
]
#original: array:5 [
"id" => 1
"libelle_domaine" => "Domaine1"
"created_at" => null
"deleted_at" => null
"updated_at" => null
]
#changes: []
#casts: array:1 [
"deleted_at" => "datetime"
]
#classCastCache: []
#attributeCastCache: []
#dates: array:1 [
0 => "deleted_at"
]
#dateFormat: null
#appends: []
#dispatchesEvents: []
#observables: []
======> #relations: array:1 [ <====== relation OK, eagerload OK
"actualite" => Illuminate\Database\Eloquent\Collection {#690
#items: array:1 [
0 => App\Models\Portail\Actualite {#669
#connection: "mysql_laravel"
#table: "actualites"
#primaryKey: "id"
#keyType: "int"
+incrementing: true
#with: []
#withCount: []
+preventsLazyLoading: false
#perPage: 15
+exists: true
+wasRecentlyCreated: false
#escapeWhenCastingToString: false
#attributes: array:14 [
...
]
#original: array:14 [
...
]
#changes: []
#casts: array:1 [
"deleted_at" => "datetime"
]
#classCastCache: []
#attributeCastCache: []
#dates: array:1 [
0 => "deleted_at"
]
#dateFormat: null
#appends: []
#dispatchesEvents: []
#observables: []
#relations: []
#touches: []
+timestamps: true
#hidden: []
#visible: []
#fillable: []
#guarded: array:1 [
0 => "*"
]
#forceDeleting: false
}
]
#escapeWhenCastingToString: false
}
]
#touches: []
+timestamps: true
#hidden: []
#visible: []
#fillable: []
#guarded: array:1 [
0 => "*"
]
#forceDeleting: false
}
]
#escapeWhenCastingToString: false
}
When i do a dd() in the build() function of the Mailable, it's KO, i have not the expected datas, i have not the relationship and eagerload.
Illuminate\Database\Eloquent\Collection {#686
#items: array:1 [
0 => App\Models\Portail\Domaine {#685
#connection: "mysql_laravel"
#table: "domaines"
#primaryKey: "id"
#keyType: "int"
+incrementing: true
#with: []
#withCount: []
+preventsLazyLoading: false
#perPage: 15
+exists: true
+wasRecentlyCreated: false
#escapeWhenCastingToString: false
#attributes: array:5 [
"id" => 1
"libelle_domaine" => "Domaine1"
"created_at" => null
"deleted_at" => null
"updated_at" => null
]
#original: array:5 [
"id" => 1
"libelle_domaine" => "Domaine1"
"created_at" => null
"deleted_at" => null
"updated_at" => null
]
#changes: []
#casts: array:1 [
"deleted_at" => "datetime"
]
#classCastCache: []
#attributeCastCache: []
#dates: array:1 [
0 => "deleted_at"
]
#dateFormat: null
#appends: []
#dispatchesEvents: []
#observables: []
======> #relations: [] <====== No relation, no eagerload
#touches: []
+timestamps: true
#hidden: []
#visible: []
#fillable: []
#guarded: array:1 [
0 => "*"
]
#forceDeleting: false
}
]
#escapeWhenCastingToString: false
}
I've tried this in the mailable :
public function build()
{
$test= DomaineRepository::domaineNewsletter("2022-05-23", "2022-05-30");
dd($test);
}
It returns the expected datas with relationship and eagerload.
So, have you some idea why, in the mailable, $this->domaines from the __construct is not available with the eagerload in the build ?
It's about queue, relationship and (de)serialization.
In Laravel doc : https://laravel.com/docs/9.x/queues#handling-relationships
Any previous relationship constraints that were applied before the model was serialized
during the job queueing process will not be applied when the job is deserialized.
Therefore, if you wish to work with a subset of a given relationship,
you should re-constrain that relationship within your queued job.
For me, i've
relationship in controller --> mail queue --> serialization --> job handle --> deserialization --> mailable build
the constrain's relationship must be re-constrain in the mailable build

Laravel change collection attributes name

How would you change the attributes from a collection?
So the return is a Collection of Models with changed attributes, and not a Collection of arrays.
The things is, I would like to make some kinda of translation presenter, so when I get the collection I could dd($collection) and instead of 'name' it would show 'nome', also using response()->json($collection) would show the changed named.
so something like $presenterNames = ['name' => 'nome', 'id' => 'identificador']; and the names that aren't here would call normally.
Collection {#213 ▼
#items: array:2 [▼
0 => Category {#215 ▼
+timestamps: false
#fillable: array:2 [▶]
#connection: "mysql"
#table: "categories"
#primaryKey: "id"
#keyType: "int"
+incrementing: true
#with: []
#withCount: []
#perPage: 15
+exists: true
+wasRecentlyCreated: false
#attributes: array:3 [▼
"id" => 1
"name" => "categoria1"
"slug" => "categoria1"
]
#original: array:3 [▶]
#changes: []
#casts: []
#dates: []
#dateFormat: null
#appends: []
#dispatchesEvents: []
#observables: []
#relations: []
#touches: []
#hidden: []
#visible: []
#guarded: array:1 [▶]
#slugOptions: null
}
1 => Category {#219 ▶}
]
}
so basically I would like to change
#attributes: array:3 [▼
"id" => 1
"name" => "categoria1"
"slug" => "categoria1"
]
to this
#attributes: array:3 [▼
"identificador" => 1
"nome" => "categoria1"
"slug" => "categoria1"
]
Dinamicaly by using a assoc array.
Solution :
In your Model add below code.
protected $appends = ['nome'];
public function getNomeAttribute(){
return $this->attributes['name'];
}
You can access it by querying $modelObj->nome;
Do the same for all needed attributes.
This will also reflect in your JSON response.
Thanks
Reference : https://laravel.com/docs/5.7/eloquent-mutators#defining-an-accessor

with() relation with condition retrieve all the records- Laravel

I want to retrieve only the listed organisations but the the database return all the records.
you can see below a record should not be returned as a listed organisation I include below the code and my Model and The result I got from the database
$organisations = Organisation::with(['year_status' => function ($query) use ($year_id) {
$query->where(['year_id' => $year_id, 'is_listed' => 1]);
}])->get();
public function year_status()
{
return $this->hasMany('App\OrganisationYearStatus');
}
and what I got :
11 => Organisation {#630 ▼
#fillable: array:8 [▶]
#connection: "mysql"
#table: null
#primaryKey: "id"
#keyType: "int"
+incrementing: true
#with: []
#withCount: []
#perPage: 15
+exists: true
+wasRecentlyCreated: false
#attributes: array:14 [▼
"id" => 39
"organisation" => "Test Organisation"
"sector_id" => 1
"country_id" => 1
]
#original: array:14 [▶]
#changes: []
#casts: []
#dates: []
#dateFormat: null
#appends: []
#dispatchesEvents: []
#observables: []
#relations: array:1 [▼
"year_status" => Collection {#632 ▼
#items: []
}
]
#touches: []
+timestamps: true
#hidden: []
#visible: []
#guarded: array:1 [▶]
}
database structure :
id
organisation_id
year_id
is_listed
Make sure your relationship is well established (Organization model hasMany year_status) and that your fields are in the visible array in the YearStatus Model.
Also, the query you're using is translated to:
SELECT * FROM organizations where exists
(select * from year_status where organizaton_id = organizations.id
and year_id = $year_id and is_listed = 1)
It doesn't fetch ALL the records existing in the database, it fetches due to the relationship + the fields you've added
Note: If you want to fetch only the organization that have OrganizationYearStatus, then you should use:
Organisation::whenHas('year_status' => function ($advancedWhenHas) use ($year_id) {
$advancedWhenHas->with(['year_status' => function ($query) use ($year_id) {
$query->where(['year_id' => $year_id, 'is_listed' => 1]);
}}])->get();
You can type out ->toSql() instead of ->get() to make sure your query is the one you expect it to be

Laravel : How to add a key value into an object?

How to add a key value into an object?
I want to add "tags" => ["foo","bar"] into every record,demo:
ArticlesController.php
public function index()
{
$articles = user()->articles;
dd($articles); //This is a collection
//loop the collection,and add `"tags" => ["foo","bar"]` into every record
$multiplied = $articles->map(function ($item, $key) {
dd($item);//result below
//how to write here?
});
$newArticles = $multiplied->all();
dd($newArticles);
return view('articles', compact('newArticles'));
}
result of dd($item):
Article {#498 ▼
#fillable: array:2 [▶]
#casts: array:1 [▶]
#connection: "mysql"
#table: null
#primaryKey: "id"
#keyType: "int"
+incrementing: true
#with: []
#perPage: 15
+exists: true
+wasRecentlyCreated: false
#attributes: array:6 [▼
"id" => 1
"title" => "hello"
"content" => "hello world"
"user_id" => 2
"created_at" => "2017-07-23 15:34:52"
"updated_at" => "2017-07-23 15:34:55"
]
#original: array:6 [▶]
#dates: []
#dateFormat: null
#appends: []
#events: []
#observables: []
#relations: []
#touches: []
+timestamps: true
#hidden: []
#visible: []
#guarded: array:1 [▶]
}
question:
How to write the code in map()?
$item->tags = ['foo', 'bar'];
return $item;
inside map will do the job.
But do the map to $newArticles.
simple
$object->new_key="value";

Search value deeply nested within a Laravel Collection

I have an Eloquent Collection just like this (dumped):
Collection {#788 ▼
#items: array:3 [▼
0 => Rating {#787 ▼
#table: "ratings"
+timestamps: false
#connection: null
#primaryKey: "id"
#keyType: "int"
#perPage: 15
+incrementing: true
#attributes: array:3 [▼
"id" => 1
"rating" => "50"
"type" => "min"
]
#original: array:3 [▶]
#relations: []
#hidden: []
#visible: []
#appends: []
#fillable: []
#guarded: array:1 [▶]
#dates: []
#dateFormat: null
#casts: []
#touches: []
#observables: []
#with: []
#morphClass: null
+exists: true
+wasRecentlyCreated: false
}
1 => Rating {#786 ▶}
2 => Rating {#785 ▶}
]
}
In one of my views, I need to show only the Ratings with 'max' type and I am trying to filter the collection without success until now.
My tries so far:
if ($ratings && $ratings -> search('max') != false)
OR
if ($ratings && $ratings -> contains('max'))
So... What is the smart and eloquent way of achieving this?
Thanks in advance.
You can use the where method: https://laravel.com/docs/5.4/collections#method-where
$filtered_ratings = $ratings->where('type', 'max');
You could also use the filter() method on the collection.
return $collection->filter(function ($value, $key) {
if($value->type == 'max'){
return $value;
}
});

Resources