laravel relationship eagerload mailable - laravel

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

Related

Laravel Eloquent (A Model attribute won't show)

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 ???

Laravel - Hash check returns false even when its correct

I was using md5 to login from a form and I am trying to switch to bcrypt, but the Hashk::check method always returns false, even if the password is correct, any idea why it is not working?
$email = Input::get('email');
$password = Input::get('password');
$user = User::where("email","=",$email)->first();
if(Hash::check($password,$user->password)) {
$userID = $user->id_user;
$username = $user->first_name." ".$user->last_name;
$admin = "yes";
Session::put('userID',$userID);
Session::put('userName',$username);
Session::put('admin',$admin);
return redirect('/cw-admin/');
} else {
return Redirect::to('/cw-admin/login')
->withErrors(['no' => 'Incorrect password']);
}
}
EDIT added the User model, I only changed the fillables, primaryKey and ID, the methods are left empty.
class User extends Model implements Authenticatable
{
protected $table = 'user';
protected $primaryKey = 'id_user';
protected $fillable = [
'first_name','last_name','password','email'
];
protected $hidden = [
'password', 'remember_token',
];
}
dd output:
User {#592 ▼
#table: "user"
#primaryKey: "id_user"
#fillable: array:4 [▼
0 => "first_name"
1 => "last_name"
2 => "password"
3 => "email"
]
#hidden: array:2 [▼
0 => "password"
1 => "remember_token"
]
#connection: "mysql"
#keyType: "int"
+incrementing: true
#with: []
#withCount: []
#perPage: 15
+exists: true
+wasRecentlyCreated: false
#attributes: array:7 [▶]
#original: array:7 [▶]
#changes: []
#casts: []
#dates: []
#dateFormat: null
#appends: []
#dispatchesEvents: []
#observables: []
#relations: []
#touches: []
+timestamps: true
#visible: []
#guarded: array:1 [▼
0 => "*"
]
}
You have to check your User table definition. If Hashed field is returning false even if you are posting something to it, it is not retrieving the same value as the one you have posted.
In my case, I would use max length on column(s) that store hashed values.
Alter Table Users Modify password VCHAR(255);

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