Laravel Eloquent can increment ($collection->increment('field')) fail? - laravel

I have a redirect function that looks like this. For some reason, it looks like it's sometimes missing to increment the item. I'm wondering if it sometimes can be so that it misses this because some caching or something?
public function redirect($key)
{
$item = $this->items->findOrFail($key);
$item->increment('redirects');
$encode = urlencode($item->_url);
return view('item.redirect', ['url' => ($encode)]);
}

I'm not sure about topic of your question. Do you get such method somewhere from Laravel?
I don't know what is $key here but you should make sure this is single id (for example 2) and not array.
For single model:
$item->increment('redirects');
will work
but if $key sometimes is array then
$item = $this->item->findOrFail($key);
will be collection, so increment method would cause error, because there is no increment method on Eloquent collection.

Related

Laravel filtered collection is no longer filtered after json encoding

I have an Eloquent collection with an eager loaded relationship. When I filter this eager loaded relationship, it works fine, but if I encode it as JSON (to pass it to my front end Javascript framework), the collection is no longer filtered.
Simplified example:
$questions = Question::with('variables')->get();
foreach($questions as $key => $q) {
$questions[$key]->variables = $q->variables->reject(function($v) {
return $v->type == 3;
});
}
dd($questions);
If I look at the $questions variable at this point, my collection is correctly filtered. If, however, I add json_decode(json_encode($questions)) following line before dumping, the collection is no longer filtered.
Note that in my real application, I have to do some things with the rejected variables before throwing them out of the collection, so I cannot simply filter them out during the query.
My workaround right now is to json encode and decode the collection, then do an array filter to get rid of the variables I do not want to pass to the front end. This works, but seems like a terribly inelegant and unnecessary solution. Am I doing something wrong or is this the expected behavior?
I'm still running Laravel 5.8 on this application in the event that this behavior has changed on newer versions.
Why not just load the variables twice then?
$questions = Question::with(['variables' => fn($v) => $v->where('type', '!=', 3)])->get();
// do whatever you need to do with the filtered collection
// reload variables relationship
$questions->load('variables');
// do whatever you need to do with the question and all its variables.
You can try
$questionsWithFilteredVariables = $questions->map(function($question) {
$variables = $question->variables->reject(fn($var) => $var->type === 3);
unset($question->variables);
$question->variables = $variables;
return $question;
});
//Now do json_decode(json_encode(...)), it will still contain filtered variables
$questionsWithFilteredVariables = json_decode(
json_encode($questionsWithFilteredVariables)
);

Laravel Bulk Detach

First, I use detach() in laravel with this approach and this is work!
$student = \App\StudentRegistrars::withTrashed()->findOrFail($id);
$student->father_registrars()->detach();
$student->mother_registrars()->detach();
But when I am trying to mass detach in laravel, I can't do that.
$students = \App\StudentRegistrars::whereIn('id', $ids);
$student->father_registrars()->detach();
$student->mother_registrars()->detach();
Any solution about this problem?
There's quite a few issues with your code. You're close, but not quite there.
First, you need to be aware of what your variable is. Currently, this is not working, as $student is not defined. You called your variable $students (with an 's'), so make sure you're using the right variable.
Secondly, at the point you're calling detach(), your $students variable is an instance of the Builder class, not a single StudentRegistrars instance. You need to finalize your query with a closure:
$students = \App\StudentRegistrars::whereIn('id', $ids)->get();
Thirdly, the detach() method only works on a single instance, so if you called $students->father_registrars()->detach(), it would still fail.
You need to iterate your results and call detach() one-by-one:
foreach ($students as $student) {
$student->father_registrars()->detach();
$student->mother_registrars()->detach();
}
You final code would therefore be:
$students = \App\StudentRegistrars::whereIn('id', $ids)->get();
foreach ($students as $student) {
$student->father_registrars()->detach();
$student->mother_registrars()->detach();
}
There are more efficient approaches to mass detachment, such as directly querying and deleting records from the pivot table (or tables):
DB::table('pivot')->whereIn('column', $ids)->delete();
But this answer serves to fix your logic issues.

Laravel 5.5 - Deep Relations calling by -->

I now spent hours googling and experimenting on trying to get an relation with two intermediate tables working.
My database looks like this:
(apt_id is apartment_id in real, was shorter to write)
I have every relation one away setup correctly with belongsTo and and hasMany:
EXAMPLE FROM House.php
public function user()
{
return $this->belongsTo('App\User');
}
public function apartments()
{
return $this->hasMany('App\Apartment');
}
Isn't there a way to access these relations like:
$house->apartments->tenants->entries
in Blade:
#foreach ( $house->apartments->tenants->entries as $entry )
, since I want to display all house entries on house.show (Blade View)
The only way it's working is by using a bunch of foreach inside each others... :/ and they define the order...
Using my wanted relation calling produces:
Property [tenants] does not exist on this collection instance.
displayed on the page.
Greetings,
Pat
I don't think you can achieve what you want using the code you posted, because when calling, for example, $house->apartments it returns a Collection object. So, it is not dealing with database anymore, that's why you would need to use a bunch of #foreachs.
I don't know if this is the best way to solve this, or if it will help you in your actual problem, but you could think this problem backwards and try something like this:
$entries = \App\Entry::whereHas('tenants', function($q) use ($house) {
$q->whereHas('apartments', function($q1) use ($house) {
$q1->where('apartments.house_id', $house->id);
});
})->get();
And in the view:
#foreach ($entries as $entry)
{{ $entry->tenant->apartment->house->name }}
#endforeach

Get Magento defined values for model "sales/order" status

I'm trying to perform some operations on an order if its status/state is "processing" (not sure if I should use status or state here .. any help on this would be great too).
Running a ->getStatus() on a sales/order model tells me the value I'm looking for is "processing", but I don't want to compare this to a string literal. I would like to compare it to the Magento defined value, so I'm wondering how it determines this value "processing" and how can I do the same?
Here is some code..
$order = Mage::getModel('sales/order')->loadByIncrementId($orderID);
if($order->getStatus() == "processing") {
//do stuff
}
I would like to change "processing" to something like Mage::getModel('sales/order')->STATUS_PROCESSING
I'm sure the answer lies somewhere in the Mage_Sales_Model_Order::getStatus() method, but I'm having a very hard time locating that method. I've even used ReflectionClass to view the methods and do not see it!
The following code will go off of the Magento status code:
$orders = Mage::getModel('sales/order')->getCollection()
->addFieldToFilter('status', 'Processing')
;
foreach ($orders as $order) {
//do stuff
}
I hope that is what you are looking for.

How to increment a column using Eloquent Model in Laravel 4

I am not sure how to increment the value in a column using Eloquent Model in Laravel 4?
This is what I currently have and I am not sure how correct is this.
$visitor = Visitor::where('token','=','sometoken')->first();
if(isset($visitor)){
$visitor->increment('totalvisits');
}else{
Visitor::create(array(
'token'=>'sometoken',
'totalvisits'=>0
));
}
With Query Builder we could do it using
DB::table('visitors')->increment('totalvisits');
Looks like the code that I posted worked after all
$visitor = Visitor::where('token','=','sometoken')->first();
if(isset($visitor)){
$visitor->increment('totalvisits');
}else{
Visitor::create(array(
'token'=>'sometoken',
'totalvisits'=>0
));
}
Prior to a fix a few weeks ago the increment method actually fell through to the query builder and would be called on the entire table, which was undesirable.
Now calling increment or decrement on a model instance will perform the operation only on that model instance.
Laravel 5 now has atomic increment:
public function increment($column, $amount = 1, array $extra = [])
{
if (! is_numeric($amount)) {
throw new InvalidArgumentException('Non-numeric value passed to increment method.');
}
$wrapped = $this->grammar->wrap($column);
$columns = array_merge([$column => $this->raw("$wrapped + $amount")], $extra);
return $this->update($columns);
}
which essentially works like:
Customer::query()
->where('id', $customer_id)
->update([
'loyalty_points' => DB::raw('loyalty_points + 1')
]);
Below is old answer for Laravel 4 where the built-in increment was a seperate select and then update which of course leads to bugs with multiple users:
If you'd like to accurately count your visitors by ensuring the update is atomic then try putting this in your Visitor model:
public function incrementTotalVisits(){
// increment regardless of the current value in this model.
$this->where('id', $this->id)->update(['totalVisits' => DB::raw('last_insert_id(totalVisits + 1)')]);
//update this model incase we would like to use it.
$this->totalVisits = DB::getPdo()->lastInsertId();
//remove from dirty list to prevent any saves overwriting the newer database value.
$this->syncOriginalAttribute('totalVisits');
//return it because why not
return $this->totalVisits;
}
I'm using it for a change tag system but might work for your needs too.
Does anyone know what to replace the "$this->where('id',$this->id)" with because since dealing with $this Visitor it should be redundant.

Resources