can't orderBy accessor in Laravel - php

I cant orderBy points. Points is accessor.
Controller:
$volunteers = $this->volunteerFilter();
$volunteers = $volunteers->orderBy('points')->paginate(10);
Volunteers Model:
public function siteActivities()
{
return $this->belongsToMany(VolunteerEvent::class, 'volunteer_event_user', 'volunteer_id', 'volunteer_event_id')
->withPivot('data', 'point', 'point_reason');
}
public function getPointsAttribute(){
$totalPoint = 0;
$volunteerPoints = $this->siteActivities->pluck('pivot.point', 'id')->toArray() ?? [];
foreach ($volunteerPoints as $item) {
$totalPoint += $item;
}
return $totalPoint;
}
But I try to sortyByDesc('points') in view it works but doesn't work true. Because paginate(10) is limit(10). So it doesn't sort for all data, sort only 10 data.
Then I try to use datatable/yajra. It works very well but I have much data. so the problem came out
Error code: Out of Memory

You could aggregate the column directly in the query
$volunteers = $this->volunteerFilter();
$volunteers = $volunteers->selectRaw('SUM(pivot.points) AS points)')->orderByDesc('points')->paginate(10);

Related

PHP best performance to delete duplicate array values

In my web application I have a function that is executed more than 100 times in a minute, and I'm trying to find a better performing approach.
public static function removeDuplicateFeeds($id, $feeds, $todo)
{
$actionsHistory = ActionsHistory::whereAccountId($id)->whereActionName($todo)->get();
if (!empty($actionsHistory)) {
foreach ($actionsHistory as $history) {
foreach ($feeds as $key => $feed) {
if ($history->action_name == $feed['pk']) {
unset($feeds[$key]);
}
}
}
}
}
I want to remove all the elements from $feeds that are in $actionsHistory as well.
UPDATE:
in this test code first index of $feeds array as "pk":"7853740779" is stored on my database and after remove duplicate this item should be removed, but i have all of $feeds items into $filteredFeeds too
$userAccountSource = InstagramAccount::with('user', 'schedule')->get();
$feeds = [
json_decode('{"pk":"7853740779","username":"teachkidss","full_name":"..."}'),
json_decode('{"pk":"7853740709","username":"teachkidss","full_name":"..."}'),
json_decode('{"pk":"7853740009","username":"teachkidss","full_name":"..."}')
];
$filteredFeeds = AnalyzeInstagramPageController::removeDuplicateFeeds($userAccountSource[0]->id, $feeds, 'like');
public function removeDuplicateFeeds($id, $feeds, $todo)
{
$feeds = collect($feeds); // If $feeds is not already a collection
$actionsHistory = ActionsHistory::whereAccountId($id)
->whereActionName($todo)
->whereIn('action_name', $feeds->pluck('pk')) // retrieves only duplicate records
->select('action_name') // reducing the select improves performance
->get(); // Should return a eloquent collection instance
if (!empty($actionsHistory)) {
return $feeds->whereNotIn('pk', $actionsHistory->pluck('action_name'));
}
return $feeds;
}
Without knowing the number of feed or database records you'll have to test these for performance against your dataset and see if these are more performant.
public static function removeDuplicateFeeds($id, $feeds, $todo)
{
$feeds = collect($feeds); // If $feeds is not already a collection
$actionsHistory = ActionsHistory::whereAccountId($id)
->whereActionName($todo)
->select('action_name') // reducing the select improves performance
->get(); // Should return an eloquent collection instance
if (!empty($actionsHistory)) {
return $feeds->whereNotIn('pk', $actionsHistory->pluck('action_name'));
}
return $feeds;
}
or if your database query returns significantly more records than you have feeds you could try leveraging mysql's faster query speed instead of using php's slower array_filter/foreach speed.
public static function removeDuplicateFeeds($id, $feeds, $todo)
{
$feeds = collect($feeds); // If $feeds is not already a collection
$actionsHistory = ActionsHistory::whereAccountId($id)
->whereActionName($todo)
->whereIn('action_name', $feeds->pluck('pk')) // retrieves only duplicate records
->select('action_name') // reducing the select improves performance
->get(); // Should return a eloquent collection instance
if (!empty($actionsHistory)) {
return $feeds->whereNotIn('pk', $actionsHistory->pluck('action_name'));
}
return $feeds;
}
If either of these works, it would be good to know how much faster this was for you. Let us know. Good luck.
You may try array_unique function from PHP. Source.
You can use build in laravel unique() function from collection, see more here :
https://laravel.com/docs/5.6/collections#method-unique
doing that you cove will maintain clean and elegant.

Laravel Collection Loop to another Collection

How to loop the eloquent collection from one to another? I'm just getting first line of array. I have more than 4 array in the collection.
$queries = Students::where('year',"=", 1)->get();
$students = new Students();
foreach ($queries as $query) {
$students->name = $query->name;
$students->faculty = $query->faculty ."Add something";
$students->year = $query->year;
}
dd($students);
I want to change the collection a bit before I print to json. For example, I want add something behind the faculty
Use the transform() method to modify collection:
$students = Students::where('year', 1)->get();
$students->transform(function($i) {
$i->faculty = $i->faculty . 'add something';
return $i;
});
You also can use resource classes to transform the data before returning JSON response.
You could use map() to modify collection-
$queries = $queries->map(function($query){
$query->faculty = $query->faculty."kfjhgli";
return $query;
});
return $queries;

How to `count` a query result and send it as an array in JSON format?

Details
I want to
Count all my distributors that I query
Send it along within the JSON file
I know that I have 89 distributors in total, I did this dd(count($distributors));
I am sure what is the best practice for this.
Here is what I have tried
I initialize $count = 0;
Increment by 1 every time the loop execute $count = $count + 1;
Send the result toArray 'count' => $count->toArray() as part of my distributors array
Here is my code
public function index()
{
$distributors = [];
$count = 0;
foreach(
// Specific Distributors
Distributor::where('type','!=','OEM')->where('type','!=','MKP')
->get() as $distributor){
// Variables
$count = $count + 1;
$user = $distributor->user()->first();
$distributors[$distributor->id] = [
'user' => $user->toArray(),
'distributor' => $distributor->toArray(),
'hq_country' => $distributor->country()->first(),
'address' => $distributor->addresses()
->where('type','=','Hq')->first()->toArray(),
'count' => $count->toArray()
];
}
return Response::json($distributors);
}
Result
The code won't run, due to my $distributor array is not exist ...
It will run, if I take 'count' => $count->toArray() off .
Updated
I am using Laravel 4.0
The code is part of my UrlController.php
It really doesn't make a lot of sense to add this kind of count to your result. It is much simpler to just send the result and let the client do the counting. Because the information on how much distributors you actually have is right in your array of distributors. It's just it's length.
Here's an example with javascript (and $.ajax although that doesn't really matter)
$.ajax({
url: 'get/distributors',
method: 'GET'
}).done(function(data){
var count = data.length;
});
Model:
class Distributor extends Eloquent {
public function country()
{
return $this->hasOne('Country');
}
public function addresses()
{
return $this->hasMany('Address');
}
public function hqAddress()
{
return $this->addresses()->where('type', 'Hq')->first();
}
public function user()
{
return $this->hasOne('User');
}
}
Controller:
$distributors = Distributor::whereNotIn('type', ['OEM', 'MKP'])
->with('country', 'user')->get();
$count = 0;
$distributors->each(function(Distributor $distributor) use(&$count) {
$distributor->count = $count;
$count++;
});
return Response::json($distributors);
Sorry, I can be wrong.
I am not laravel expert.
But what is this fragment is about?
this Index function is part of Model?
#evoque2015 is trying to put some custom array into $distributors[$distributor->id]?
if that is the goal, could you do any test with any simple value of 'count' that sholud work in your opinion?
My guess is : 'count' index is not acceptable for your Distributor::where function
(if it is acceptable - show as the value of 'count' that doesn't break your code even if return wrong result/data ).
So I would try to change the name of this parameter either to 'my_custom_count',
should it be declared somewhere in Distributor model declaration?
Found this :
Add a custom attribute to a Laravel / Eloquent model on load?
It seems to prove my guess.
So we need to change model class like:
class Distributor extends Eloquent {
protected $table = 'distributors';//or any you have already
public function toArray()
{
$array = parent::toArray();
$array['count'] = $this->count;
return $array;
}
.....
}
and probably more changes in model or just add 'count' column to the table :-)

How can I get the return value of a Laravel chunk?

Here's an over-simplified example that doesn't work for me. How (using this method, I know there are better ways if I were actually wanting this specific result), can I get the total number of users?
User::chunk(200, function($users)
{
return count($users);
});
This returns NULL. Any idea how I can get a return value from the chunk function?
Edit:
Here might be a better example:
$processed_users = DB::table('users')->chunk(200, function($users)
{
// Do something with this batch of users. Now I'd like to keep track of how many I processed. Perhaps this is a background command that runs on a scheduled task.
$processed_users = count($users);
return $processed_users;
});
echo $processed_users; // returns null
I don't think you can achieve what you want in this way. The anonymous function is invoked by the chunk method, so anything you return from your closure is being swallowed by chunk. Since chunk potentially invokes this anonymous function N times, it makes no sense for it to return anything back from the closures it invokes.
However you can provide access to a method-scoped variable to the closure, and allow the closure to write to that value, which will let you indirectly return results. You do this with the use keyword, and make sure to pass the method-scoped variable in by reference, which is achieved with the & modifier.
This will work for example;
$count = 0;
DB::table('users')->chunk(200, function($users) use (&$count)
{
Log::debug(count($users)); // will log the current iterations count
$count = $count + count($users); // will write the total count to our method var
});
Log::debug($count); // will log the total count of records
$regions = array();
Regions::chunk(10, function($users) use (&$regions ) {
$stickers = array();
foreach ($users as $user)
{
$user->sababu = ($user->region_id > 1)? $user->region_id : 0 ;
$regions[] = $user;
}
});
echo json_encode($regions);
Use this custom function to get return value from chunked data
function iterateRecords($qb, int $count = 15)
{
$page = 1;
do {
$results = $qb->forPage($page, $count)->get();
$countResults = $results->count();
if ($countResults == 0) {
break;
}
foreach ($results as $row) {
yield $row;
}
unset($results);
$page++;
} while ($countResults == $count);
}
How to use it
$qb = User::select();
$users = iterateRecords($qb, 100);
foreach ($users as $user) {
echo $user->id;
}
Total Users Count $totalUsersCount = $qb->count();

Static method, Zend_View error

I have a static method 'findAll' on a model which basically gets all rows with certain criteria. This method works fine and I can call it using:
$m::findAll();
Where $m is the model name as a variable. I can output this and it returns correct results. However, when assigning this to a variable in the Zend_View object, as:
$this->view->viewvariable = $m::findAll();
I get the error:
Zend_Db_Table_Exception: Too many
columns for the primary key
Any ideas why?
Find all function:
final public static function findAll($where = false, array $options = array()) {
$object = new static();
if (!empty($options)) $options = array_merge($object->options, $options);
else $options = $object->options;
$run = $object->buildDefaultSelect($where, $options);
$rows = $run->fetchAll();
if ($options['asObject'] == true) {
$result = array();
foreach ($rows as $r) {
$class = new static();
$class->setInfo($r);
$result[] = $class;
}
return $result;
} else {
if (count($rows) > 0) return $rows;
else return array();
}
}
Note: This function works fine everywhere apart from when assigning to a view variable. If I run the below (not assigning it to a view variable), it shows the correct array data.
var_dump($m::findAll($module['where'], $module['options']));
exit;
In my view (I have replaced the actual name with viewvariable for the sake of this post):
<?php foreach($this->viewvariable as $item) { ?>
//Do some echoing of data in $item
//Close foreach
I doubt the issue is with Zend_View. It's hard to tell without seeing your code, but my guess is that findAll() is using the Zend_Table_Db find() function incorrectly.
To my knowledge, the only place that throws that exception is the find() function on Zend_Db_Table_Abstract.
Perhaps, inside the findAll() function (or in a function it calls) you're doing one of these:
$zendDbTable->find(1,2) //is looking for a compound key
$zendDbTable->find(array(1,2)) //is looking for two rows
When you really want the opposite.

Categories