laravel eloquent order by multiple columns and show in one column - php

I have 4 tables:
user
particular
pro
advertiser
I have a table where i show the users data.
in this table i have a column named Name where i use the code below
<td>
#if($user->pro))
{{$user->pro->company_name}}
#elseif($user->particular))
{{$user->particular->name." ".$user->particular->last_name}}
#elseif($user->advertiser)
{{$user->advertiser->company_name}}
#endif
</td>
So this column is popuplated from 3 different tables and different column.
How can i sort this column table.

You're going to need a complex ORDER BY clause that you won't be able to set up using Eloquent's methods. However, Eloquent builder offers orderByRaw() method that let's you use raw SQL expression in your ORDER BY clause. Combine that with a couple of SQL IF's (for MySQL see https://dev.mysql.com/doc/refman/5.1/en/control-flow-functions.html#function_if) and you should get what you need.
In your case it would be sth like:
->orderByRaw("IF(user.is_pro,pro.company_name,IF(user.is_particular, particular.name, IF(user.is_advertiser,advertiser.company_name,'')))")
Keep in mind that ordering by such expressions might have negative performance impact and that using raw SQL might make your code less portable.

Related

Select rows with equal values

How to do in Laravel?
1) get from table rows where user_id has the equal values?
2) return sum of some_amount values from this selected rows
Table:
- id;
- user_id;
- some_amount;
Table has bunch of certificates with some amount of money that belongs to different users. I need to find all certificates that belongs to one user (one user can have few certificates) and count how much money he have from all his certificates
Given you aren't looking for a solution to query this for an individual user, it sounds like you want to group by the user and sum the result of certificates.
The answer from #PhilCross is pretty close, you'd just need to modify it to add the group clause and remove the where condition. Something like this:
ModelName::groupBy('user_id')->sum('some_amount');
or
\DB::table('table_name')->groupBy('user_id')->sum('some_amount');
Generally eloquent or the query builder will have a method that relates to how you would do this in raw SQL.
I find it helpful to write out or think about how I would write the raw SQL and then slowly fill the eloquent or query builder in from that.
If you use a Model:
ModelName::where('user_id', 1)->sum('some_amount')
If you're using the query builder:
\DB::table('table_name')->where('user_id', 1)->sum('some_amount');
This is the documentation for the query builder:
https://laravel.com/docs/5.6/queries
Models: https://laravel.com/docs/5.6/eloquent
Model Relationships: https://laravel.com/docs/5.6/eloquent-relationships
Collections: https://laravel.com/docs/5.6/collections

LEFT JOIN CONCAT('hello_',c.id) possible?

IS it possible to have something like this:
SELECT c.id as id
FROM Channels c
LEFT JOIN CONCAT('hello_',c.id)
I NEED this concat with the c.id. THERE IS NO OTHER WAY.
So any tips?
You asked whether you can use an expression involving column values and functions like CONCAT to generate table names in queries in MySQL.
The short answer is no.
There's a longer answer involving MySQL prepared statements. That's basically a way to use string processing in a MySQL stored procedure to generate the text of a query to run.
If you intend to do it at SQL level: it is not possible. Table name tokens are not computed from expressions.
However, with php and creating a query dynamically, it should be quite trival to use a "modified" table name according to your need.
So, your concat() should be a php expression when building the query.
In the case where you are trying to join a (different) table based on the column value of a base table, you are far from any SQL semantics.
In that case you might want to rearrange your schema to merge all table instances identified by what your concat is now trying to compute info a single table and label each row with the logical table it belongs to.

How do I get the comment of a specific column with Doctrine ORM

Is there a way to retrieve the column's comment (like from INFORMATION_SCHEMA.COLUMN table on MySQL) from a certain table without actually "hardcode" the query with Doctrine ORM?
Finally I found an way. what I was trying to do was get a column comment from inside a Controller
//lets say we have a table named 'product'
//and we want to get the comment from the 'name' column
//first we get a list of columns from 'product'
$columns = $this->getDoctrine()->getEntityManager()->getConnection()->getSchemaManager()->listTableColumns('product');
//then we just access getComment function from the 'Column' class
//for the 'name' column:
echo $columns['name']->getComment();
I doubt it, and the same would go for Propel as well. Both use PDO, and afaik there is no database-neutral way to do this.
To achieve this, you can often query the table columns using SELECT statements on system tables (certainly Oracle and PostgreSQL allow this). However, the names of the tables involved differ from one vendor to another. Here is how to get column names, for example, in PostgreSQL - getting comments would probably be quite similar.
It would be quite possible for an ORM to offer what you want, but the underlying implementation would be the approach I outline above. Perhaps you could request it on the Doctrine mailing list?

MySQL and PHP: Searching for rows through a field in an undefined number of joined tables

There is an example table, 'main_table' with the fields 'ID', 'some_data'.
There is an aggregate table, 'agg' with the fields 'id', 'main_table_id', 'joinee_id'.
And then there is the final table, 'joinee', with the fields 'id', 'email'.
The tables 'main_table' and 'joinee' are in a many:many relationship, through 'agg'.
I would like to be able to search all the 'main_table' entries by 'email' from 'joinee', without doing a left join and then group by 'main_table'.'id'.
The final result needs to list all the 'main_table' entries, once per entry. Imagine it like this - I would like 'main_table' to get a temporary field "participants" which would contain all the 'emails' - I would then perform a LIKE match on this field in the same query that does this, in order to find the 'main_table' entries that have anything to do with the email I entered.
Is this possible?
Mind you, this is only a fragment of a much larger query. 'main_table' is already joined with 5 other tables, and their fields are already used as filters. Thing is, I know there can be only one joined table in each of those cases - with 'joinee', the number of connected entries varies.
Thank you.
Combining row results into single cells is something the relational model does not support, and most DBMSes are exceptionally bad at it. If you were to go down that road, you'd need to pull a considerable amount of hacking, like using user-defined functions or nonstandard syntax features to combine the values.
But if I understand you correctly, your problem is that you need to find entries in the main table that have desirable entries in their related email rows, and the problem is that there's a many-to-many relation.
How about this:
Write a query that finds the emails you're interested in and inner join that on the agg table. This will give you the related main_table_ids. Use distinct or group by to remove doubles.
Use the query from 1 as a subquery, and plug it into the query as you have it now, using something like WHERE main_table.id IN (/* subquery */), or, alternatively, inner join your existing query on the subquery from step 1. Which of these you use depends on the circumstances; traditionally, subqueries are slower than joins (all else being equal), but it may be the other way around, depending on your structure and actual data. On some older DMBSes, buffering the subquery result in a temporary table can prove beneficial.

Is there a way to make a table name dynamic in a query?

I am trying to create a Class-Inheritance design for products.
There is the base table that contains all the common fields. Then for each product type there is a separate table containing the fields that are for that product type only
So in order to get all the data for a product I need to JOIN the base table with whatever table that correlates to the product_type listed in the base table. Is there a way to make this query join on the table dynamically?
Here is a query to try to illustrate what I am trying to do:
SELECT * FROM product_base b
INNER JOIN <value of b.product_type> t
ON b.product_base_id = t.product_base_id
WHERE b.product_base_id = :base_id
Is there a way to do this?
No, there's no way to do this. The table name must be known at the time of parsing the query, so the parser can tell if the table exists, and that it contains the columns you reference. Also the optimizer needs to know the table and its indexes, so it can come up with a plan of what indexes to use.
What you're asking for is for the table to be determined during execution, based on data found row-by-row. There's no way for the RDBMS to know at parse-time that all the data values correspond to real tables.
There's no reason you would do this to implement Class Table Inheritance. CTI supports true references between tables.
You're instead describing the antipattern of Polymorphic Associations.
Make 2 queries:
First select < value of b.product_type > and then use it in the second one (the one that you have, but replace < value of b.product_type > with the result from the first one).
No. There would be little point even if it were possible, as the query optimiser would not be able to make a plan without knowing anything about the right- hand side of the join.
You need to construct the query using concatenation or similar, but make sure that you only use a valid table name to avoid injection attacks.
You can create a procedure that takes the table name as an argument and constructs a dynamic-SQL query. But it's probably easier to do this in your server-side code (PHP). But rather than make it a variable (and as suggested vulnerable to injection attacks), create separate classes for the different join combinations. Use another class (like a dispatcher) to determine the correct class to instantiate.

Categories