I need to use DB::select() statement for a complex query I´m working on that is not returning any results. After some diggin I found out the reason was that the model_type field comparison was not working regardless wheather I use this sintaxis:
select *
from questions
where model_type = "App\Models\Product"
Or this sintaxis:
select *
from questions
where model_type = "App\\Models\\Product"
Even though the second sentence works perfectly on MySQL Workbench, it returns no results in Laravel.
The MySQL table field name is of course model_type and the values are hold as: App\Models\Product and all relationships work perfectly. It seems that there should be a way to specify to Laravel how to compare the values, but I haven´t found it.
Please note that I have to use DB::select() as oposed to Eloquent, since my actual query deals with MySQL MATCH() AGAINST() and some other complexities.
Any help would be welcome. Txs!
Just in case someone else runs into this predicament, I still don´t know why Eloquent is not able to resolve it´s model, but I did find this great article which solves this and other potential situations as well, by decoupling Laravel logic from the information stored in the database. https://josephsilber.com/posts/2018/07/02/eloquent-polymorphic-relations-morph-map#the-default-morph-type
Related
I have been trying to achieve this for some time, and it's not been working. I had to go with a temporary solution of sql join statement (which, as you can imagine, isn't working as expected)
I am required to perform multiple mysql queries, and most of the time, I will need the result from one query for another. For example:
let sql = "SELECT * FROM users";
con.query(sql,(err,users)=>{
let anothersql = "SELECT * FROM posts WHERE uuid="+users.uuid+";
con.query(anothersql,(err,posts)=>{ callback(200,{'posts',posts,'users'=>users}) })})
This actually worked when I have just one user, but I need to perform this action on all users in the database, hence I need to fetch all users with their associated posts then return then all in that same order, i.e. result 1 => {'users1':users,'post1':posts},{'users2':users,'post2':posts}
I know there are ways to achieve this using promise, async etc, but I don't know how to do it. If anyone can point me in the direction of a tutorial or sample code, that would be much appreciated.
UPDATE:
If you stumble on this question, don't do it manually, use Sequelize. Seriously, use Sequelize.
I'm trying to optimise a large MySQL query. I accidentally found out that a query with all fields listed (SELECT Orders.id AS Orders__id, <...>; the default CakePHP behaviour) takes 4 times longer compared to a query with just SELECT * FROM - 0.324 seconds vs. 0.084 seconds; checked several times.
I'm wondering if I can disable this behaviour. I've tried:
adding 'fields' => '*' to the find() options or calling ->select('*'), but it results in SELECT Orders.* AS Orders__* which throws an SQLSTATE[42000] error.
getting rid of the aliased title with ->select(['*' => '*']) as per query-builder.html#selecting-data, but that results in SELECT * AS * which also throws an error
using ->enableAutoFields(false)
I also tried to Google but I don't even know how to call this
It seems you want to override the benefits of the ORM so I'm going to suggest a method that should not be used in normal operations.
$datasource = ConnectionManager::get('default');
$datasource->execute('SELECT * FROM some_table;');
If you want to hydrate entities you must allow the select statement to alias the fields so the above will not give you entities.
My opinion is that you should use the normal select and optimize your caching strategies for this data.
Apparently, I can't not have the fields listed because of how CakePHP ORM was designed.
So instead, solved by manually whitelisting only the fields I actually need. The query is still relatively fast after all (around 100 ms. according to my measurements).
Till today I was relying on Laravel relationships, but since I opened mysql logs I was very disappointed.
When I execute code
Company::with(['users', 'machines'])->get()
mysql.log looks this way
select * from `company` where `company`.`id` = '48' limit 1
select * from `user` where `user`.`company_id` in ('48')
select * from `machine` where `machine`.`company_id` in ('48')
Why Laravel does not use joins for eager fetching? Also, are there any ways of improving perfomance and still using Laravel Models?
I know that Doctrine ORM eager loading works pretty nice by using joins.
Thank you for your help.
If you really want to use joins instead of the Eloquent computed queries, I suppose you could just use the fluent query builder (that comes shipped with Laravel through the DB facade) and stick that code into a method of your model to keep everything nice and SRP.
For instance:
class Company extends Model {
public function sqlWithJoin() {
$users = DB::table('company')
->leftJoin('user', 'company.id', '=', 'user.company_id')
->get();
return $users;
}
}
This would generate a proper join query for you.
As for why you would want to do this, you would have to benchmark both options to see which one gives you the best performance for your specific data. I wouldn't generalize that one option always has better/worse performance than the other.
As stated in the comments, I'm not sure performance-wise why this is the preferred method, but from usability, being able to access a model's relationships as it's own separate property is much easier than working with a join, especially in the event of a many-to-one relationship.
Let's compare the above example using both ->with() and ->leftJoin() methods.
When using ->with() every relationship is defined as a property of Company, accessed via $company->users. It's easy to run a foreach() loop over this property foreach($company->users AS $user) and output information, such as username, email, etc. Also, if the Company has no users, you don't have to worry about displaying empty values (especially important on chaining models using . notation, such as users.user_details).
Now, looking at leftJoin(). If you try to chain multiple leftJoins() on each model and their sub-models, there's a chance you won't get the results you're expecting. Essentially, leftJoin() doesn't handle NULL records as well as individual queries can.
Next, to output a list of a company's users, you would have to run a loop such as:
foreach($company AS $row){
echo $row->username;
echo $row->email;
// etc etc
}
This becomes problematic as Eloquent doesn't handle duplicate properties well at all. For example, if the company has an email field as well as the user, it's anyone's guess which is actually displayed. Unless you do a selectRaw("companies.email AS email, users.email AS user_email)", only one email property is going to be returned. This also applies to columns like id, where multiple are going to be fetched by using leftJoin(), but only one will actually be accessible.
Long story short, leftJoin() comes with the potential for a lot of issues when trying to join multiple tables with the possibility of duplicate information, null information, etc. While the performance of running multiple queries using the ->with() method may not be the best, it allows for easier use in retrieving and displaying information.
I've had to query some weird database extracted from an excel file, it's pretty bad designed. This has leaded me to some strange needs for the ActiveRecord, one of those is setting conditions when eager loading relations.
So here's my problem and my weird solution.
1.- I include the relation
ModelOne.includes(:relation)
2.- I try to set conditions on the columns of the included table
ModelOne.includes(:relation).where("relation.some_column = something")
And I get the following error
Mysql2::Error: Unknown column some_column in where clause ...
The error, of course, displays the query which contains no joins or something that refers to the included table.
Now, in the other hand, this works:
ModelOne.includes(:relation).where(relation: { some_column: "something" })
This sintax with hashes it's cool but doesn't support LIKE queries, for example, so... the strange thing is that, after passing a hash including the included table, every reference to the columns of this table works. Let's say, for what I've found, this is how I would do a LIKE query:
ModelOne.includes(:relation).where.not(relation: { id: nil }).where("some_column LIKE ?","")
Note the weird not where, it does nothing in terms of conditioning the query, but I need it so I can use the columns of the eager loaded table in following methods (LIKE's, groups and so)
What is the right way of doing this? Why does Rails behave like this? What am I doing wrong? When does Rails actually include the eager loaded table?
Note: Using the joins method it's not an option in this case for different reasons
http://guides.rubyonrails.org/active_record_querying.html
Read 13.2 Specifying Conditions on Eager Loaded Associations
Using where like this will only work when you pass it a Hash. For
SQL-fragments you need use references to force joined tables:
Article.includes(:comments).where("comments.visible = true").references(:comments)
I'm trying to get columns of a table while getting sum of another related table (I'm using a MySQL database). Tables are Advantages and Conversions, Advantages has a conversionID column to join. They are all related via models. I first tried to achieve this with Eloquent method, but I could not succeed, so I came up with this Fluent method, which is working fine:
DB::table('conversions')
->join('advantages','advantages.conversionID','=','conversions.id')
->where('conversions.used','=',0)
->group_by('conversions.id')
->get(array(
'conversions.*',
DB::raw('SUM(advantages.point) as totaladvantage')
))
I guess the query describes how the columns are and what I want to achieve.
So my question is: Is there a more efficient way to achieve this? Using DB::raw for this seemed weird to me, and sum() method only returns sum of the columns. This is the almost only place where I wrote both fluent and raw query in my project, so that made me think if I'm missing something.
Thanks in advance
You can call aggregate methods through fluent
DB::table('conversions')
->join('advantages','advantages.conversionID','=','conversions.id')
->where('conversions.used','=',0)
->group_by('conversions.id')
->select(array(
'conversions.*',
'advantages.point as totaladvantage')
)
->sum('advantages.point')
Havent tested this, so let me know how you get on