How to use query function to fix slow performance laravel datatable? - laravel

I would like to use the query function, because I have a slow performance in laravel datatable. I have this select:
$audits = \OwenIt\Auditing\Models\Audit::select(
'audits.id',
'audits.user_type',
'audits.user_id',
'audits.event',
'audits.auditable_id',
'audits.auditable_type',
'audits.old_values',
'audits.new_values',
'audits.created_at',
'users.login'
)
->join('users','users.id','audits.user_id');
and I tried to use the query function this way:
$audits = \OwenIt\Auditing\Models\Audit::query()->select(
'audits.id',
'audits.user_type',
'audits.user_id',
'audits.event',
'audits.auditable_id',
'audits.auditable_type',
'audits.old_values',
'audits.new_values',
'audits.created_at',
'users.login'
)
->join('users','users.id','audits.user_id');
But is not working the code above. I thought to use Laravel Pagination, however in the project that I work has datatables in so many tables and I want to keep the same standard. Someone can help me?

You have a misunderstanding of what query() does. Simply put it does nothing differently and it will not help you with any optimization for your queries.
Model::select(args)
When you do Model::select('col1', 'col2'), this is what happens:
There is no Model::select public static function, so __callStatic(method: 'select', parameters: ['col1', 'col2']) gets called instead.
All __callStatic does in Model is try to call a public function method passing in parameters.
# Illuminate\Database\Eloquent\Model
public static function __callStatic($method, $parameters)
{
return (new static)->$method(...$parameters);
}
There is no Model::select public function, so __call(method: 'select', parameters: ['col1', 'col2']) gets called instead.
All __call does is forward the call to the object returned from the public function Model::newQuery. So, (new Model)->newQuery()->select(['col1', 'col2'])
# Illuminate\Database\Eloquent\Model
public function __call($method, $parameters)
{
if (in_array($method, ['increment', 'decrement'])) {
return $this->$method(...$parameters);
}
if ($resolver = ($this->relationResolver(static::class, $method))) {
return $resolver($this);
}
return $this->forwardCallTo($this->newQuery(), $method, $parameters);
}
To summarize, when you do Model::select(args), it's the same as doing (new Model)->newQuery()->select(args).
Model::query()->select(args)
This is the source code for the Model::query public static function.
public static function query()
{
return (new static)->newQuery();
}
As you can see, it's the same.
The only "optimization" that comes out of using query() is making the call stack shorter.
Model::select()
calls __callStatic
calls __call
calls forwardCallTo
calls newQuery
Model::query()->select()
calls newQuery
There are still a few extra steps that happen between newQuery and select, but they are the same.
As for possible optimizations:
Creating an index on audits 's user_id column
Pagination
Lazily loading the results (cursor, lazy)
https://laravel.com/docs/9.x/eloquent#cursors
https://laravel.com/docs/9.x/eloquent#chunking-using-lazy-collections
Loading the data through ajax
https://datatables.net/reference/option/ajax

Related

Model function call in a Template

I have model called Page and view called view.blade.php
//this is a model
public function Test()
{
return 'test';
}
//this is the template
<h1>{{$Test}}</h1>
how can I do this? please help me?
As I understood, you want to call model function inside your view? Do it like this:
{{Page::Test()}}
Edit:
If you need to use $this in your function to pass some data for your function (if that was what you were asking in comments below), you can do something like this. First define your static function:
public static function getPages()
{
return [
//some logic (get all pages)
];
}
Now, let's say this function will return multiple pages. If you want to filter them, and to display only one page on your view, you can pass the id of that view as a parameter to a next function which you will then pass to your view:
public function getSinglePage()
{
return self::getPages()[$this->id];
}
Lastly, in order to display the output of that function, use the same method as above, with new function name:
{{Page::getSinglePage()}}

How to Override Update Method in Laravel 5.8

I'm trying to create a ticketing system that's linked to timesheets. Whenever someone updates a ticket, they have the option of submitting how much time has been spent on it, in a time_spent form object. Timesheets are polymorphically linked to many objects.
I want to create a trait, CreatesTimesheets, then apply that to relevant models so that:
Each of those models gets a timesheets() function.
It overrides the update() method of each model that it's a trait of, to check whether any time was submitted in time_spent.
It's the second bit that isn't working. My code is as below, and when I update the model (which works fine), this code doesn't fire at all, even testing it with a simple dd().
How do I fix this?
<?php
namespace App\Traits;
use App\Models\HR\Timesheet;
use Auth;
trait CreatesTimesheets
{
public function update(array $attributes = [], array $options = [])
{
dd('test');
if ($request->time_spent)
{
$timesheet = new Timesheet;
$timesheet->time_logged_in_mins = $request->time_spent;
$timesheet->appointment_id = Auth::user()->appointedJobIDToUse();
$this->timesheets()->save($timesheet);
}
parent::update($attributes, $options);
}
public function timesheets()
{
return $this->morphToMany('App\Models\HR\Timesheet', 'timesheetable');
}
}

Returning same variable to every controller in laravel

I need to send the same result to almost every view page, so I need to bind the variables and return with every controller.
My sample code
public function index()
{
$drcategory = DoctorCategory::orderBy('speciality', 'asc')->get();
$locations = Location::get();
return view('visitor.index', compact('drcategory','locations'));
}
public function contact()
{
$drcategory = DoctorCategory::orderBy('speciality', 'asc')->get();
$locations = Location::get();
return view('visitor.contact', compact('drcategory','locations'));
}
But as you see, I need to write same code over and over again. How can I write it once and include it any function whenever I need?
I thought about using a constructor, but I cannot figure out how I can implement this.
You are able to achieve this by using the View::share() function within the AppServicerProvider:
App\Providers\AppServiceProvider.php:
public function __construct()
{
use View::Share('variableName', $variableValue );
}
Then, within your controller, you call your view as normal:
public function myTestAction()
{
return view('view.name.here');
}
Now you can call your variable within the view:
<p>{{ variableName }}</p>
You can read more in the docs.
There are a few ways to implement this.
You can go with a service, a provider or, like you said, within the constructor.
I am guessing you will share this between more parts of your code, not just this controller and for such, I would do a service with static calls if the code is that short and focused.
If you are absolutely sure it is only a special case for this controller then you can do:
class YourController
{
protected $drcategory;
public function __construct()
{
$this->drcategory = DoctorCategory::orderBy('speciality', 'asc')->get();
}
// Your other functions here
}
In the end, I would still put your query under a Service or Provider and pass that to the controller instead of having it directly there. Maybe something extra to explore? :)
For this, you can use View Composer Binding feature of laravel
add this is in boot function of AppServiceProvider
View::composer('*', function ($view) {
$view->with('drcategory', DoctorCategory::orderBy('speciality', 'asc')->get());
$view->with('locations', Location::get());
}); //please import class...
when you visit on every page you can access drcategory and location object every time
and no need to send drcategory and location form every controller to view.
Edit your controller method
public function index()
{
return view('visitor.index');
}
#Sunil mentioned way View Composer Binding is the best way to achieve this.

Laravel Controller member variable

Is there any possibility in laravel to keep the state of the controller object?
In every example I found, the controller looks the following way:
class MyController extends Controller {
public function getView(){ //return the view }
public function postData() { //save the data }
}
What I would do is to call a service which loads specific data from my data base and return it to the view. In the example above this should be done within the getView() function. What if I need the same data in my postData() function.. Then I have to make another database call in the postData function. It is not possible to have a member variable in 'MyController' and to load the data only once for the class because of routing in laravel. When I call via routing the getView function I get another instance of MyController than I get if I call postData. Is there a possibility to call a specific function only once for the whole controller and to get access to this values from all the functions within the controller?
Is there a possibility to call a specific function only once for the
whole controller and to get access to this values from all the
functions within the controller?
As per my understanding it it not possible. Actually any function of controller is being called via routes. When your any route has been called every time the new object of controller is being created. But it has other way of round. You can use Cache. You can implement it as below:
Call to your specific function of controller.
Get the data from the database.
Store it in Cache for other functions.
In other functions check is data available in Cache? then get from Cache else call your database function to get the data.
Simply in coding as below:
Use Cache;
class MyController extends Controller {
public function getView(){
$data = call_to_database_and_returned_data();
Cache::put('data',$data,120);
return the view
}
public function postData() {
$data = null;
if(Cache::has('data')) {
$data = Cache::get('data');
} else {
$data = call_to_database_and_returned_data();
Cache::put('data',$data,120);
}
}
}
Syntax Description:
Cache::put('name_to_save_data',$your_variable_to_save,for_number_minutes);

Is there any decent way to Decorate models returned from a Magento `[model]_load_after`event?

I'm trying to overwrite some methods in models, and I'm on a mission to avoid overwrites and rewrites of models for maximum compatibility with other modules.
I figured the best way would be to simply decorate models after they are loaded from Magento, however as far as I can tell because of the way the observer pattern in Magento is written it's impossible to accomplish this. ( As Magento always returns the reference to $this ), and the lack of interfaces might also cause trouble later down the road? See this partial of Mage/Core/Model/Abstract.php
/**
* Processing object after load data
*
* #return Mage_Core_Model_Abstract
*/
protected function _afterLoad()
{
Mage::dispatchEvent('model_load_after', array('object'=>$this));
Mage::dispatchEvent($this->_eventPrefix.'_load_after', $this->_getEventData());
return $this;
}
My question boils down to the title, is there a decent way of accomplishing this?, or am I simply stuck with rewrites :(?
The path I would like to take is;
On event [model]_load_after
return new Decorator($event->getObject())
Where the decorator class in my case would be something like;
public function __construct(Mage_Sales_Model_Order_Invoice $model)
{
parent::__construct($model); // sets $this->model on parent class, see below
}
// overwrite the getIncrementId method
public function getIncrementId()
{
return '12345';
}
// partial of parent class
public function __call($method, array $args)
{
return call_user_func_array(array($this->model, $method), $args);
}
And just some pseudo-code for extra clarification;
$model = Mage::getModel('sales/order_invoice')->load(1);
echo get_class($model);
Namespace_Decorator **INSTEAD OF** Mage_Sales_Model_...
echo $model->getIncrementId();
'12345' **INSTEAD OF** '1000001' ( or whatever the format might be )
Thanks for your time reading / commenting, I really hope there actually is a way to accomplish this in a clean fashion without making use of code overrides or rewrites of models.
Edit: extra clarification
Basically what I would like is to return an instance of the Decorator in a few cases, the sales_invoice being one of them and customer the other. So when any load() call is made on these models, it will always return the instance of the Decorator instead of the Model. Only method calls that the decorator overrides would be returned, and any other method calls would "proxied" through __call to the decorated object.
I'm not sure if I got your question right but here goes.
I think you can use the event [model]_load_after and simply do this:
$object = $event->getObject();
$object->setIncrementId('12345');
Or if you want to use a decorator class make it look like this:
public function __construct(Mage_Sales_Model_Order_Invoice $model)
{
parent::__construct($model);
$model->setIncrementId($this->getIncrementId());
}
public function getIncrementId()
{
return '12345';
}
I know that this is not exactly a decorator pattern but it should work.
I know that when adding a new method to the 'decorator' class you need to add it to attach data to the main model.
This is just my idea. I haven't got an other.
[EDIT]
You can try to rewrite the load method on the object to make it return what you need. But I wouldn't go that way. You can end up screwing a lot of other things.
I don't think there is an other way to do it because load always returns the current object no mater what you do in the events dispatched in the method. see Mage_Core_Model_Abstract::load()
public function load($id, $field=null)
{
$this->_beforeLoad($id, $field);
$this->_getResource()->load($this, $id, $field);
$this->_afterLoad();
$this->setOrigData();
$this->_hasDataChanges = false;
return $this;
}
By making it return new Decorator($this), you might achieve what you need, but just make sure that when calling $model->doSomething() and doSomething() is not a method in your decorator you still end up calling the original method on the model.

Resources