display a list with the highest points appearing first - php

I am displaying a list of items where I want the highest point to appear first irrespective of how the data is sorted. At first the snippet that does the logic is working but when inputed with different variables as shown in the table structure, the expected logic output fails. Here is the entity structure
books.php
id | name | points | active
1 | huk | 5 | true
2 | foo | 2 | true
3 | doo | 2 | true
4 | jack | 12 | true
Here is my controller snippets
$restresults = $this->getDoctrine()
->getRepository('xxxBundle:Books')
->findBy(
['active' => true],
['points' => 'DESC']
);
the above snippets result is unpredictable from my coding intention, as it returns this result in sorting in respect to book points 5, 2, 2, 12 instead of 12, 5 , 2 , 2.
Please what could be wrong with my algorithm

Looking at sorting result it looks like the string type is sorted instead of int.
To fix that change the type of column points to int both in doctrine and db for example with #Column(type="integer") */ depending on your style of mapping.
The other way is to make DQL. If you can't do it then you need to make your own DQL
and write orderby there.
Ex.
$books = $em->getRepository('xxxBundle:Books')
->createQueryBuilder('q')
->addSelect('ABS(q.points) AS HIDDEN pointsOrder') // other way CAST(q.points AS UNSIGNED)
->orderBy('pointsOrder', 'DESC)
->getQuery()
->getResult();

Related

How to sum a colum from a related model efficiently on Laravel

Ok I got this table
affiliates_referral_clicks
id | affiliate_id | clicks | date
1 | 1 | 10 | 2021-07-14
2 | 1 | 2 | 2021-07-11
3 | 2 | 1 | 2021-07-11
4 | 2 | 14 | 2021-07-10
...
Of course my Model Affiliate has a relationship with referralClicks
Affiliate.php
public function referralClicks(){
return $this->hasMany(AffiliateReferralClick::class,'affiliate_id');
}
Now I want to bring all Affiliates with the SUM of all their clicks that have a date between a given date. I implemented it like this
$affiliate = Affiliate::with(['referralClicks' => function($query) use($params) {
$query->whereDate('date','>=', $params['dateFrom'])
->whereDate('date','<=', $params['dateTo'])
->select('clicks')
;
}])->get();
foreach ($affiliates as $affiliate){
$affiliate->totalClicks = $affiliate->referralClicks->sum('clicks');
}
this works fine, but since the affiliates_referral_clicks table is waaaay too big and the request ends up being too slow, I think if you do the query without using Eloquent's helpers you can get a much faster query.
So my question would be...how can I do the same I just did but with raw querys (or whatever the most efficient way is)? Im using a MySQL DB I hope you guys can help me!
Haven't tried that yet but that's how I'd solve this (if we assume, you only need the sum and nothing else from the relationship):
$affiliate = Affiliate::withSum(['referralClicks.clicks as totalClicks' => function($query) use($params) {
$query->whereDate('date','>=', $params['dateFrom'])
->whereDate('date','<=', $params['dateTo'])
->select('clicks')
;
}])->get();

How to check with validate if a Value does exist per User that it is only once contained?

I am creating a coffee shop website because I want to learn Laravel and I like coffee.
I have a dashboard where the user can manage his coffee. He can manually select from all kind of coffee. With my approach, I detect 2 Major Problems which is connected to the question.
1. Problem
Let's say the User has added 2 coffees with the id of 1 and 2
id | user_id | selected_coffee | created_at | updated_at
----------------------------------------------------------------
1 | 1 | 1 | xxxxxxxxxx | xxxxxxxxxx
1 | 1 | 2 | xxxxxxxxxx | xxxxxxxxxx
If the User selects in these orders because he can select multiple. * Let's say he select the coffee id 1, 2 and the last one 3.*
Because the code detects that the first id is in the database, it is ignoring the other values.
2. Problem
Let's say the User1 has added 2 coffees with the id of 1 and 2
id | user_id | selected_coffee | created_at | updated_at
----------------------------------------------------------------
1 | 1 | 1 | xxxxxxxxxx | xxxxxxxxxx
1 | 1 | 2 | xxxxxxxxxx | xxxxxxxxxx
The User2 can select the coffee but it is not written in the Database which I need for display visually on the frontend
I know it is not well seen to add 2 Problems but my thought is if we can fix the number 1. The problem then the 2.Problem will disappear.
Here is my code which makes it unique:
public function store(Request $request)
{
app('App\Http\Controllers\ScoreController')->store($request);
$message = ['selected_coffee.unique' => 'Check Coffee ID already exist'];
$this->validate($request,[
'selected_coffee' => 'unique:manage_coffees',
],$message);
if($request->input('checkedDrink') != null){
foreach ($request->input('checkedDrink') as $selected_id) {
$manage_coffee = new ManageCoffee();
$manage_coffee->user_id = \Auth::user()->id;
$manage_coffee->selected_coffee = (int)$selected_id;
$manage_coffee->save();
}
}
return redirect('dashboard/coffee');
}
I found the validator in StackOverflow but it was not well explained. I think I must do a where clause in the ->validate but I don't know where I should put it.
Hopefully, you guys can help me.
Torsten
If I understood you correct you need exist rule but with additional condition like in example from official docs.
Something like that:
use Illuminate\Validation\Rule;
$this->validate($request,[
'selected_coffee' => [
Rule::exists('manage_coffees')->where(function ($query) {
$query->where('user_id', Auth::user()->id);
//you can specify any conditions you like
}),
],
],$message);

Database select one common field if conditions match

I'm building Laravel 5.4 web application and I have below database table:
==================================
product_id|attribute_id|option_id
==================================
1 | 1 | 1
1 | 2 | 3
1 | 4 | 10
2 | 1 | 1
2 | 2 | 4
... etc
So i submit form with attributes id and options id so i can build array from it or whatever.
What i want to achieve that I select from the database the product_id which match exact combination for example:
[
attribute_id => 1,
option_id => 1
attribute_id => 2,
option_id => 3
attribute_id => 4,
option_id => 10
]
This condition only apply to product with product_id = 1
Don't know if i can do it using database query or by php.
First, make a model that reflects your data. Then use the Eloquent query builder to get the data you're looking for. If you need just one number returned that matches, make sure to add on to the end the query "->distinct()".
You may also pass an array of conditions to the 'where' clause.
Your code may look something like this:
$match = DB::table('products')
->where('attribute_id', 1)
->where('option_id', 1)
->distinct()
->get();
https://laravel.com/docs/5.4/queries#introduction
If you just want the product with product_id = 1
Assumed you have stored this in "product_attribute_option" table
and its fields are product_id |attribute_id | option_id as you shown.
$query = DB::table('product_attribute_option as pao');
$query->where('pao.product_id', 1);
$results = $query->get();

Yii2: ActiveRecord with distinct query

I need to write an ActiveRecord query where I get all fields of rows without duplicates in one field.
Example: Here is a table of books. I want to get all data of the rows with
distinct isbn. From duplicates the first row should be taken. The result should be the rows with id 1,2,4
id | title | isbn
--- | ------- | ------
1 | hello | 1001
2 | world | 1002
3 | this | 1002
4 | is | 1003
5 | funny | 1003
My first attempt was
$books = Books::find()->select('isbn')->distinct()->all();
but that fills only the isbn field in $books[0], $books[1], ....
Is this possible using Yii2 ActiveRecord?
You may use groupBy() for this:
$books = Books::find()->groupBy(['isbn'])->all();
But this returns random row with matching isbn value and may not work in some DBMS. Your requirements are too ambiguous to handle it in predictable way. But if you want always first row, you may use subquery to fetch matching isbn and id:
$query = (new Query())
->select(['MIN(id) as id, isbn'])
->from(Books::tableName())
->groupBy(['isbn']);
return Books::find()
->alias('book')
->innerJoin(['subquery' => $query], 'book.id = subquery.id AND book.id = subquery.id')
->all();
The description given in Yii2 for the distinct property is as follows:
Whether to select distinct rows of data only. If this is set true, the SELECT clause would be changed to SELECT DISTINCT.
-- https://www.yiiframework.com/doc/api/2.0/yii-db-query#$distinct-detail
therefore you should pass true with distinct property if you need to select the distinct values of 'isbn' as follows:
$books = Books::find()->select('isbn')->distinct(true)->all();

Match multiple columns from a form

I'm doing a price table, and I was wondering if there is a way to match multiple columns with different values base on the values from a form... please allow me.
Table: Price
id | name | color | class | year | price
________________________________________
1 | BMW | Red | XML | 2001 | 1200
2 | BMW | Red | XML | 2003 | 1201
3 | BMW | Blue | LXX | 2004 | 1230
4 | VW | Red | LXS | 2001 | 1100
5 | VW | Blue | LXV | 2003 | 1103
Basically my table looks like that, the idea is to get the price value by matching four columns...
if name is bmw and color is red and class is xml and year is 2001 then the price is 1200
How do I make that query?... I don't have any code for this type of query just yet... but I was thinking to do a few queries base on the first value....
SELECT * FROM price
WHERE name = '$user_query' AND (color = 'Red' OR class = 'XML' )
if [...] ... that just wont work...
I don't know how to do that query... I can go one by one matching the inputs from the form but I will have to make many queries... four queries actually... I'd love to simplified my query to get that value from column price base on the request from the user...
The other thing is to use switch case : value from the first query ... or use if, elseif .... r just IF ... that is because I know the values that the user will chose from the form and the form has only 4 selects ... one for each "column" name, color, class and year
You may think that I want you to do this for me... well yes and no... yes because I want to learn how to do this type of queries and NO because I have this combinations using IF's without a data base... I'd like to use a data base...
if ($name == 'bmw' && $color == 'red' && [...]) {
$price = 1200;
} [...] and so on...
about 23 elseif {} ...
Thank you for taking the time and share your thoughts on this idea...
** E D I T **
So far I have two good answers and I'm using both, and for that I have a bit of trouble choosing the right answer...
Mahmoud Gamal and Rajesh Paul
So I'm going to leave it like that for a few more days whichever gets more votes if any, thats the one that I'm going to choose...
Thank you.
if it's about reducing the query length then I suggest this-
SELECT * FROM price
WHERE (name, color, class) = ('$user_query', 'Red', 'XML' );
Just grouping the set of attributes instead of cascading the conditions using AND, OR definitely reduces the length of the query.
You must change the syntaxis
WHERE name = '$user_query' AND (color = 'Red' OR class = 'XML' )
WHERE name = '$user_query' AND color = 'Red' AND class = 'XML'
What you need here is a optional parameters in the WHERE clause:
SELECT *
FROM price
WHERE ($ColorPatam IS NULL OR Color = $ColorParam)
AND ($ClassParam IS NULL OR Class = $ClassParam)
...
If one of the parameters has a NULL values, then the expression is ignored.

Categories