Model's scope function is showing wrong value, but only in production server - laravel

I'm facing a weird problem which only happen in the production server (It's 100% normal in my local). I'm using Laravel 8, and have a model named Student. Inside the model, I created several scope methods like this:
public function scopeWhereAlreadySubmitData($query)
{
return $query->where('status_data_id', '!=', 1);
}
public function scopeWhereAlreadySubmitDocument($query)
{
return $query->where('status_document_id', '!=', 1);
}
Then I use the methods above in an AJAX controller to chain it with count method. Something like:
public function getAggregates()
{
$student = Student::with(['something', 'somethingElse']);
$count_student_already_submit_data = $student->whereAlreadySubmitData()->count();
$count_student_already_submit_document = $student->whereAlreadySubmitDocument()->count();
return compact('count_student_already_submit_data', 'count_student_already_submit_document');
}
Here's is where the weird thing happens: while the controller above produce the correct value in my local, in production count_student_already_submit_data has a correct value but count_student_already_submit_document has zero value. Seeing at the database directly, count_student_already_submit_document should have value more than zero as there are many records has status_document_id not equals to 1.
I've also tried to use the scopeWhereAlreadySubmitDocument method in tinker. Both in local and production, it shows the correct value, not just zero.
Another thing to note is that Student model actually had 4 scope methods like the above, not just 2. 3 of them is working correctly, and only 1 is not. Plus, there's another controller using all the scope methods above and all of them are showing the correct value.
Have you ever face such thing? What could be the problem behind this? Your input is appreciated.

Turns out this is because the query builder is effected by the given chain. The weird thing I mentioned about "local vs production" is likely doesn't gets noticed in the local because its only has a few records. I should've use clone() when chaining the builder. See the topic below for more information.
Laravel query builder - re-use query with amended where statement

Related

How to access one relation inside another relation in laravel?

I have a query in which I have eagar loaded two models using with function like this:
ModelA::with(['relationB', 'relationC.relationC.A'])->where(condition)->get();
So, ModelA has two relations like this:
public function B(){ return $this->blongsTo(B::class);}
public function C(){ return $this->blongsTo(C::class);}
Now, my requirement is that I want to add a condition in B() function like this:
public function C() {
if($this->B->status) {
return $this->blongsTo(C::class)->withTrashed();
}
return $this->blongsTo(C::class);
}
But it return null on line this statement:
if($this->B->status)
Here is the error message
Trying to get property 'status' of non-object
My ultimate requirement is that using one relation function I want to fetch deleted records and non deleted based on the condition, but somehow it is not working.
My laravel application version is 7.30.4.
A relational function (such as your public function C()) works a bit of magic under the hood. This is because really it is designed to be called in a query way like you show already with the ::with(['relationB', ...]).
However, because of this, if you were to eager load C, then $this is not yet loaded as the full model, and therefore B is not defined (this is assuming that modelA always has a B relation). If you were to dd($this) while performing your query, you'd see that the result would be a model without any attributes.
Getting this to work from within a relational function (with the goal of eager loading) is very difficult. You're probably better off doing the logic elsewhere, with a second query for example. This is because within the relational function, there is no way to know who or what the potential target is. However, if you only use it after modelA is loaded, then it works without issues.
You can do some things with a whereHas, but then you'd still have to do 2 queries, or you can try and see if you can get it done with an SQL IF statement, but that will not result in a relation.

decrease stock quantity at order placement in laravel

I want to decrease quantity from products table when order is added to cart ,right now when I add something to cart, correct quantity is not decreasing. For example if a I place order of 6 items then a random amount gets decreased from my products table.
Here is my code in order controller of livewire:
public function IncrementQty($cartId)
{
$carts=Cart::find($cartId);
$carts->increment('product_qty',1);
$updatePrice=$carts->product_qty * $carts->product->price;
$carts->update(['product_price'=>$updatePrice]);
$getProductStock=Product::where(['id'=>$carts['product_id']])->first()->toArray();
$newStock=$getProductStock['quantity'] - $carts['product_qty'];
Product::where(['id'=>$carts['product_id']])->update(['quantity'=>$newStock]);
$this->mount();
}
Being a beginner I am unable to understand about what's wrong here. Should I be substracting it from orderDetails table instead of carts table? If yes then how?
A couple of things to note first,
You should not call $this->mount() or any other lifecycle hooks from Livewire. If there are code inside your mount-method you need to run again, then abstract it into a separate method, and call it from both your mount() method and your IncrementQty() method.
You can use model-route-binding to inject the correct model into the method
public function IncrementQty(Cart $cart)
You can then simplify your code by using the product relation on the cart directly, and calling decrement() on that.
public function IncrementQty(Cart $cart)
{
$product = $cart->product;
$cart->increment('product_qty', 1);
$cart->update(['product_price' => $cart->product_qty * $product->price]);
$product->decrement('quantity', 1);
$this->mount(); // Do not call mount manually, abstract the contents you need into a separate method instead
}
If you are still experiencing issues, then it cannot possibly be this piece of code, or you are misreading your data somehow - in any case, if you are still experiencing issues, you will need to provide additional information with your table-structure, models, how you call this method, and data before and after your actions.
it seems you have a problem in this line:
$newStock=$getProductStock['quantity'] - $carts['product_qty'];
$carts is an object not an array, so, do:
$newStock = $getProductStock['quantity'] - $carts->product_qty;

Laravel queryScope

I created a queryScope
public function scopeCtmpActive($query)
{
return $query->where('ctmp_active', 'y');
}
Then I replace following line
$customtemplates_collection = Auth::user()->customtemplates->where('ctmp_active', 'y')->sortByDesc('ctmp_id');
with
$customtemplates_collection = Auth::user()->customtemplates->ctmpActive()->sortByDesc('ctmp_id');
And I am getting following FatalErrorException
Call to undefined method Illuminate\Database\Eloquent\Collection::ctmpActive()
How am I suppose to use a query exception with a relationship?
As the name implies, "query" scopes are for reusable, common "query" constraints.
$customtemplates_collection = Auth::user()->customtemplates;
This returns a collection. You are getting all "customtemplates" that belong to the authenticated user. Then, Laravel is nice in that the Collection class allows for a nice way to filter out results, which is why the next part works:
$customtemplates_collection = Auth::user()->customtemplates->where('ctmp_active', 'y');
You are using PHP. Not MySQL. To emphasize, you are getting every single "customtemplates" that belongs to the user, and then using (PHP) Laravel Collection's where method to go through each one and filter out the results. You are not adding a where clause to the query. That's why the above works.
However, query scopes are for query constraints so they need to happen during the query, not after. What you probably want is something like this:
$customtemplates_collection = Auth::user()->customtemplates()->ctmpActive()->orderBy('ctmp_id', 'desc')->get();
When you add the paranthesis after customtemplates(), you are invoking the customtemplates method. In this case, I'm assuming it's a HasMany relationship so it'll return a HasMany instance. Then basically, it uses PHP's magic method (__call) to build the query builder so each method after that is essentially prepping the database query. Then, when you're finished building the query, you call get to fetch the results.

Session variable on refresh

I have laravel controller like this:
public function postSessionTopic() {
$article_id = Input::get('article_id', 0);
$comment_id = Input::get('comment_id', 0);
\Session::set('page_topic_id', $article_id);
\Session::set('page_comment_id', $comment_id);
\\comment - I have tried \Session::put too, but that doesn't change anything
}
I use it, when user click on a article. I print_r out my session variable in this controller and everything looks fine. But after that I refresh my page, and there I read value from session, and sometimes it load old value or doesn't load anything. I can't understand why, because in controller i can see, that correct value is saved!
In my page, i get that value like this:
\Session::get('page_topic_id', 0)
Probably you do something wrong. You should make sure that in both cases you uses exactly same domain (with or without www).
In this controller when you don't have any input you set to session variables 0. This can also be an issue if you launch this method when you don't have any input.
You could try with adding this basic route:
Route::get('/session', function() {
$page_topic = Session::get('page_topic_id', 1);
$page_comment = Session::get('page_comment_id', 1);
echo $page_topic.' '.$page_comment.'<br />';
$article_id = $page_topic * 2;
$comment_id = $page_comment * 3;
Session::set('page_topic_id', $article_id);
Session::set('page_comment_id', $comment_id);
});
As you see it's working perfectly (but you need to remove session cookie before trying with this path).
You get
1 1
2 3
4 9
8 27
and so on. Everything as expected
Answer was - two ajax at one time. Don't do that, if you store something in session.
The session in Laravel doesn't consider changes permanent unless you generate a response (and that's the result of using symphony as it's base). So make sure your app->run() ends properly and returns a response before refreshing. Your problem is mostly caused by a die() method somewhere along your code or an unexpected exit of PHP instance/worker.
This is probably not your issue but if you are storing your laravel session in the database their is a limit on how large that value can be. The Laravel session migration has a field called "payload" that is a text type. If you exceed the limit on that field the entire session gets killed off. This was happening to me as I was dynamically adding json model data to my session.
Schema::create('sessions', function (Blueprint $table) {
$table->string('id')->unique();
$table->text('payload');
$table->integer('last_activity');
});
How much UTF-8 text fits in a MySQL "Text" field?

How can I retrieve the latest question of each thread in Propel 1.6?

I want to get the newest entries for each of my threads (private messaging system) with Propel 1.6 making use of the fluid ModelQuery interface. This would allow me to reuse both methods for getting newest entries and only getting entries where a user is involved (nobody wants to see messages not for him).
I already found out that in standard-SQL I have to use a subquery to get the newest entry for each of my forum threads. I also found out that in Propel you have to use a Criteria::CUSTOM query to achieve this, but the whole Criteria::CUSTOM stuff seems to be pre-Propel-1.6, because none of the examples makes use of the new ModelQuery.
Now the problem is, that I want to make use of the concenation feature in ModelQueries, where you can attach several own methods to each other like this:
$entries = MessageQuery::create()
->messagesInvolvingUser($user) // user retrieved or sent the message
->newestFromThread() // get the latest entry from a lot of Re:-stuff
I do not think that this would still be possible if I had to use
$c = new Criteria();
$c->add([the subquery filter]);
in newestFromThread().
What’s the best method to retrieve the latest entry for each thread given the following scheme (thread_id means that all messages belong to the same correspondence, I want only one entry per thread_id):
id(INT)
title(VARCHAR)
thread_id(INTEGER)
date(DATETIME)
The current PHP-implementation looks like this:
<?php
class MessageQuery extends BaseMessageQuery {
public function messagesInvolvingUser($user) {
return $this
->where('Message.AuthorId = ?', $user->getId())
->_or()
->where('Message.RecipientId = ?', $user->getId());
}
public function newestFromThread() {
return $this;
// To be implemented
}
}
And I am using it like this:
$messages = MessageQuery::create()
->messagesInvolvingUser(Zend_Auth::getInstance()->getIdentity())
->newestFromThread()
->find();
How about ordering results by date (DESC) and to limit to one result ?
Considering the answers in a similar question about pure SQL solutions, I guess it is easiest to add a new column newest indicating which message in a communcation is the newest. This probably fits the object-oriented approach of Propel better, too. I could write my application like this then:
public function preInsert(PropelPDO $con = null) {
$this->setNewest(1);
$this->getEarlier()->setNewest(0);
return true;
}

Resources