Optimizing collection filtering - php

I have 3 models: Account - Partner - User, with the 3 in belongsToMany relations (account_user, account_partner). I would like to filter all the User partners, authorized by the User accounts.
I have wrote the basic foreach loop hell, and I would like to ask for some tips for optimizing this filtering (like evading n+1 and other problems).
$userPartners = [];
foreach (auth()->user()->accounts as $account) {
foreach ($account->partners as $partner) {
$inList = false;
foreach ($userPartners as $userPartner) {
if ($userPartner->id === $partner->id) {
$inList = true;
}
}
if (!$inList) {
$userPartners[] = $partner;
}
}
}
dd($userPartners);

Start by eager loading your relationships with load. This will avoid the N+1 problem:
$user = auth()->user()->load('accounts');
$userPartners = [];
$user->accounts->each(function ($account) use ($userPartners) {
$ids = $account->partners->pluck('id');
if (!in_array($account->id, $ids)) {
$userPartners[] = $account->partners->where('id', $account->id);
}
});

You can do something like this:
Controller.php
$userPartners = collect();
auth()->user()->accounts->each(function($account) use ($userPartners) {
$account->partners->each(function($partner) use ($userPartners) {
if(!$userPartners->contains('id', $partner->id)) $userPartners->push($partner);
});
});
dd($userPartners);

Related

Update multiple ids against single id (Laravel)

I want to update multiple Departments against one unit. I tried this method, but it's not correct.
How can I update multiple departments ids?
Form:
Request:
Controller Function:
$pre_data = UnitDepartment::where('unit_id', $request->id)->get();
if ($pre_data) {
foreach ($pre_data as $value) {
$value->delete();
}
$department = $request->department_id;
foreach ($department as $value) {
$unitDepart = new UnitDepartment();
$unitDepart->unit_id = $request->id;
$unitDepart->department_id = $value;
$unitDepart->save();
}
}
table:
I found that is the table related to departments and units.
So you can build the relationship many-to-many between them,
Create the relationship in your models,
In Unit model:
public function departments()
{
return $this->belongsToMany('App\Unit','unit_department','unit_id','department_id');
}
In Department Model:
public function units()
{
return $this->belongsToMany('App\Department','unit_department','department_id','unit_id');
}
Attach the new relationship, simply use:
Unit::find($request->unit_id)->departments()
->sync($request->department_id);
Unfortunately, you cannot use softDelete on sync().
And I don't think you need to soft delete with unit_departments. As a pivot then it should be irrelevant if it is deleted or not.
And if user update the relationship on the frequent, this table will grow fast.
If you really need to soft-delete, you can write it like this:
$department_ids = $request->department_id;
$unit_id = $request->unit_id
// soft delete the unit_departments not in request:
UnitDepartment::where('unit_id', $unit_id)->whereNotIn('department_id', $department_ids)->delete();
// insert the new department_id+unit_id relationship
$exist_department_ids = UnitDepartment::where('unit_id', $unit_id)->whereIn('department_id', $department_ids)->pluck('department_ids')->all();
$dept_ids = array_diff($exist_department_ids, $department_ids);
$depts = collect($dept_ids)->map(function($dept_id) use ($unit_id) {
return ['department_id' => $dept_id, 'unit_id' => $unit_id];
});
UnitDepartment::insert($depts);
the problem is you're sending unit_id in the request, however using $request->id in the query which is wrong.
Change every occurance of $request->id with $request->unit_id in the controller.
to select pre data correctly
use
$pre_data = UnitDepartment::where('unit_id', $request->id)->first();
i tried this
$unit = UnitDepartment::where('unit_id', $request->unit_id)->get();
foreach ($unit as $item) {
$existDepartment[] = $item->department_id;
}
$newDepartment = $request->department_id;
$result = array_diff($newDepartment, $existDepartment);
if ($result) {
foreach ($result as $item) {
$data = new UnitDepartment();
$data->unit_id = $request->unit_id;
$data->department_id = $item;
$data->save();
}
}

SilverStripe random order for ArrayList()

I know that we can randomly sort a DataList with the following:
$example = Example::get()->sort('RAND()');
But when I try to randomly sort an ArrayList it doesn't work. I can sort an ArrayList by ID DESC, but not with RAND().
Is there a way to make an ArrayList randomly sort its items?
Example:
public function AllTheKits() {
$kits = Versioned::get_by_stage('KitsPage', 'Live');
$kitsArrayList = ArrayList::create();
foreach ($kits as $kit) {
if ($kit->MemberID == Member::currentUserID()) {
$kitsArrayList->push($kit);
}
}
return $kitsArrayList;
}
In a page:
public function getKitsRandom() {
return $this->AllTheKits()->sort('RAND()');
}
This does not work in a template with <% loop KitsRandom %>
Not really. This is the best workaround I can come up with:
foreach($myArrayList as $item) {
$item->__Sort = mt_rand();
}
$myArrayList = $myArrayList->sort('__Sort');
You could randomly sort the DataList before you loop over it, instead of trying to randomly sort the ArrayList:
public function AllTheKits($sort = '') {
$kits = Versioned::get_by_stage('KitsPage', 'Live', '', $sort);
$kitsArrayList = ArrayList::create();
foreach ($kits as $kit) {
if ($kit->MemberID == Member::currentUserID()) {
$kitsArrayList->push($kit);
}
}
return $kitsArrayList;
}
public function getKitsRandom() {
return $this->AllTheKits('RAND()'));
}
As a side note, you can filter the original DataList to fetch KitsPages that relate to this MemberID in the Versioned::get_by_stage call:
public function AllTheKits($sort = '') {
$kits = Versioned::get_by_stage(
'KitsPage',
'Live',
'MemberID = ' . Member::currentUserID(),
$sort
);
$kitsArrayList = ArrayList::create($kits);
return $kitsArrayList;
}
You could also just do this:
return KitsPage::get()->filter('MemberID', Member::currentUserID())->sort('RAND()');
When you are viewing the live site this will only get the live KitPages.

map() Mongo db Query - jenssegers/laravel-mongodb

Please see my database query:
db.comments
.find({})
.forEach(function(doc) {
doc.comments.map(function(c) {
if (c.active == 1) {
c.status = 0;
}
});
db.comments.update(
{_id: doc._id},
{$set: {comments: doc.comments}});
});
I need to write with jenssegers/laravel-mongodb. please help me If anyone has an idea to solve this
class Comment extends Eloquent {}
$comments = Comment::all();
foreach ($comments as $c) {
$c->comments = array_map(function (&$com) {
if ($com->active == 1) $com->status = 0;
}, $c->comments);
$c->save();
}

cakePHP 2 afterFind for Model

I inherited code and in the Model the previous developer used the afterFind, but left it open when afterFind is executed in case of many to many relation to that table. So it works fine when getting one element from that Model, but using the relations break it.
public function afterFind($results, $primary = false) {
foreach ($results as $key => $val) {
if (isset($results[$key]['Pitch'])) {
if (isset($results[$key]['Pitch']['expiry_date'])) {
if($results[$key]['Pitch']['expiry_date'] > time()) {
$results[$key]['Pitch']['days_left'] = SOMETHINGHERE;
} else {
$results[$key]['Pitch']['days_left'] = 0;
}
} else {
$results[$key]['Pitch']['days_left'] = 0;
}
To solve this issue I added that code after the 2nd line.
// if (!isset($results[$key]['Pitch']['id'])) {
// return $results;
//
Is there a better way to solve that? I think afterFind is quite dangerous if not used properly.

Working with multiple objects of the same type (PHP)

What is the corecte way to handle with al lot objects of the same type?
Example:
When i get a list of notes from the database with zend framework i get a rowset which contains an array with note data.
If the number of notes in the database is 20 records large it's no problem to create a note object for every note in the database. But if the database contains 12.500 note records what shall i do than? Try to create 12.500 objects is possible but it's shure isn't quick enough.
Ty, Mark
This is the code i use.
Code to get the data from the database:
if (is_numeric($id) && $id > 0) {
$select = $this->getDao()->select();
$select->where('methode_id = ?', $id);
$select->order('datum DESC');
$rowset = $this->getDao()->fetchAll($select);
if (null != $rowset) {
$result = $this->createObjectArray($rowset);
}
}
createObjectArray function:
protected function createObjectArray(Zend_Db_Table_Rowset_Abstract $rowset)
{
$result = array();
foreach ($rowset as $row) {
$model = new Notes();
$this->populate($row, $model);
if (isset($row['id'])) {
$result[$row['id']] = $model;
} else {
$result[] = $model;
}
}//endforeach;
return $result;
}
Populate function
private function populate($row, $model)
{
// zet de purifier uit om overhead te voorkomen
if (isset($row['id'])) {
$model->setId($row['id']);
}
if (isset($row['type'])) {
$model->setType($row['type']);
}
if (isset($row['tekst'])) {
$model->setLog($row['tekst']);
}
if (isset($row['methode_id'])) {
$model->setSurveyMethodId($row['methode_id']);
}
if (isset($row['klant_id'])) {
$model->setCustomerId($row['klant_id']);
}
if (isset($row['gebruiker_aangemaakt_tekst'])) {
$model->setCreatedByUser($row['gebruiker_aangemaakt_tekst']);
}
if (isset($row['gebruiker_gewijzigd_tekst'])) {
$model->setUpdatedByUser($row['gebruiker_gewijzigd_tekst']);
}
if (isset($row['gebruiker_aangemaakt'])) {
$model->setCreatedByUserId($row['gebruiker_aangemaakt']);
}
if (isset($row['gebruiker_gewijzigd'])) {
$model->setUpdatedByUserId($row['gebruiker_gewijzigd']);
}
if (isset($row['datum_aangemaakt'])) {
$model->setDateCreated($row['datum_aangemaakt']);
}
if (isset($row['datum_gewijzigd'])) {
$model->setDateUpdated($row['datum_gewijzigd']);
}
$model->clearMapper();
return $model;
}
You could page your requests, so you only get a set amount of notes back each time. Although I can't see a problem with "only 12,500" objects, unless your object creation is doing something costly, i.e more queries on the database etc.
I am not so sure about your question.
For eg :
//Create a single object
$obj = new NoteObject();
//Set the properties if it differs
$obj->setX( $row );
//Make use of the method
$obj->processMethod();
So this way you just need a single object. Lets see the code if its not to give you a right answer.
Edit :
What I thought was in
protected function createObjectArray(Zend_Db_Table_Rowset_Abstract $rowset)
{
$result = array();
//Create the model here
$model = new Notes();
foreach ($rowset as $row) {
//Yes populate the values
$this->populate($row, $model);
/*
And not like saving the object here in array
if (isset($row['id'])) {
$result[$row['id']] = $model;
} else {
$result[] = $model;
}*/
//Do some calculations and return the result in array
$result[$row['id']] = $this->doSomething();
}//endforeach;
return $result;
}
I am not sure why you are keeping this object there itself. Any reuse ? the probably paginate and do :-)

Categories