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

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.

Related

Conditionally assign Laravel's controller values on Construct method

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

Extending Eloquent Models with dynamic global scopes

I have a MySQL table that receives many different Jotform reports in JSON payloads. This has helped tremendously in capturing queryable data quickly without adding to the front-end developer's workload.
I created an eloquent model for the table. I now would like to be able to create models that extend it for each Jotform we create. I feel like it will increase the readability of my code drastically.
My eloquent model is called RawDataReport. It has created_at, updated_at, data, and report name columns in the table. I want to create the model ShiftInspectionReport extending the RawDataReport.
I have two JotForm reports one is called Shift Inspection Report and one is called Need Inspection Report. Both are part of the ShiftInspectionReport model.
So I need to query the RawDataReports table for any reports matching those names. I frequently need to query the RawDataReports report_name column with either one or more report names.
To help with this I created a local scope to query the report name which accepts either a string report name or an array of string report names. Here is the local scope on the RawDataReports model.
protected function scopeReportName($query, $report_name): \Illuminate\Database\Eloquent\Builder
{
if (is_array($report_name)) {
return $query->orWhere(function ($query) USE ($report_name) {
ForEach($report_name as $report) {
if (is_string($report) === false) {
throw new \Exception('$report_name must be an array of strings or a string');
}
$query->where('report_name', $report);
}
});
} else {
if (is_string($report_name) === false) {
throw new \Exception('$report_name must be an array of strings or a string');
}
return $query->where('report_name', $report_name);
}
}
EDIT - after comments I simplified the reportName scope
protected function scopeReportName($query,array $report_name): \Illuminate\Database\Eloquent\Builder
{
return $query->whereIn('report_name',$report_name);
}
Now in my ShiftInspectionReport model, I'd like to add a global scope that can use that local scope and pass in the $report_name. But according to this article, Laravel 5 Global Scope with Dynamic Parameter, it doesn't look like Laravel global scopes allow you to use dynamic variables.
I could just create a local scope in ShiftInspectionReport but the readability would look like
$reports = ShiftInspectionReport::shiftInspectionReport()->startDate('2021-05-15')->get()
when I'd really like to be able to just call
ShiftInspectionReport::startDate('2021-05-15')->get()
Any suggestions or comments would be appreciated.
Thank you
Thanks to IGP I figured out that I can just call the local scope right from my boot function.
My extended class looks like this now and it works.
class ShiftInspection extends RawDataReport
{
use HasFactory;
protected static function booted()
{
static::addGlobalScope('shift_inspection_report', function(\Illuminate\Database\Eloquent\Builder $builder) {
$builder->reportName(['Shift Safety Inspection','Need Safety Inspection']);
});
}
}

Overriding Laravel get and first methods

I need to override above mentioned methods to skip some database records. Using where is not an option since I would have to use it every single time as there are records in database that I do not need most of the time and I am not allowed to delete them from DB. Here is my attempt of doing this:
class SomeTable extends BaseModel {
public static function first() {
$query = static::query();
$data = $query->first();
if($data && $data->type == 'migration_type') return null;
return $data;
}
public static function get() {
$query = static::query();
$data = $query->get();
foreach($data as $key => $item) {
if($item->type == 'migration_type') unset($data[$key]);
}
return $data;
}
}
The problem with this code is that it works only when direct called on model. If I am using some other functions, like where, before get or first methods, it just skips my overridden method.
What would be the right way to do this and should I put this code within model?
My question is not duplicate as in the answer from mentioned question it is said:
all queries made from Models extending your CustomModel will get this new methods
And I need to override those two functions only for specific model, not for each one in application as not all tables have type column. That's the reason why I have written them within model class.
I need to override above mentioned methods to skip some database records.
Consider a global query scope on the model.
https://laravel.com/docs/5.8/eloquent#global-scopes
Global scopes allow you to add constraints to all queries for a given model. Laravel's own soft delete functionality utilizes global scopes to only pull "non-deleted" models from the database. Writing your own global scopes can provide a convenient, easy way to make sure every query for a given model receives certain constraints.
The issue here is that the where() method on the model returns a QueryBuilder instance where get() will return a Collection instance.
You should be able to override collection's default methods by adding a macro in it's place and can be done like so...
Collection::macro('toUpper', function () {
return $this->map(function ($value) {
return Str::upper($value);
});
});
Extending the query builder instance is not so easy but a good tutorial exists here and involves overriding the application's default connection class, which is not great when it comes to future upgrades.
Because after calling where you're dealing with the database builder and theses methods inside your model aren't being called .. about the issue you might overcome it by using select instead of first directly so will deal with the builder ..
example:
SomeTable::select('col1','col2')->take(1)->get();
another thing overriding these kind of methods is not a good idea if you're working with other developer on the same project.
good luck

Laravel Eloquent Model Dynamic Stored Properties from and to a single serialized field

Using Eloquent L5.1
I'd like to not have to define a set of properties for a model. Instead having everything stored within the database as a serialized column.
In other words any property that is set to this model should before save be removed from the object and added to an array to be serialized and then saved in db to a "data" column.
In turn after retrieving the object from db the model should be hydrated with the de-serialized parameters from the data column.
I could override a good number of Illuminate\Database\Eloquent\Model methods to accomplish this and I'm willing to do so, was curious if anyone had an example of this.
An example of this might be for configuration objects that have virtually unlimited unknown values that have multiple itterations for different objects or for different users.
Put these accessors and mutators in your Model:
class YourModel extends Model
{
public function getDataAttribute($data)
{
return collect(json_decode($data,true));
}
public function setDataAttribute($data)
{
$data = (is_array($data) || is_object($data)) ? json_encode($data) : $data;
return $this->attributes['data'] = $data;
}
}
Now when you can pass data as json string, array or object
YourModel::create(['data'=>'{"working":"ok"}']);
YourModel::create(['data'=>['working'=>'ok']]);
It will work in all three cases.

Laravel — How to get string value from object via model method?

I have model House with relation hasMany HousePhotos.
I try get link to main photo from table house_photos.
class House extends Model
{
public function photos(){
return $this->hasMany('app\HousePhoto');
}
public function get_main_photo(){
return $this->photos()->where('main', true);
}
}
Controller:
$house=House::find(1);
In View i use
{{$house->main_photo()->link}}
and got error.
When i use
{{$house->main_photo()}}
i got object. How to get string value of link to photo?
First of all you need to understand the difference between the Builder object and the Collection object.
As it is now, function get_main_photo returns a Builder object (actually a Relation object, which contains a Builder).
On these objects you can call function get() to finish the query and get the results. In this case you will get a Collection object, which you will be able to iterate.
Alternatively, in this case you seem to only have one 'main photo' per house, so instead you can call first():
public function get_main_photo(){
return $this->photos()->where('main', true)->first();
}
This will return the single associated model, on which you will be able to access ->link (if the model was found).
Remember at any point while debugging you can call the convenient dd function (which dumps the object passed as parameter and terminates the applicacion) to see what type of object you are dealing with, and all its attributes.

Resources