Laravel WhereHas with two separated databases - mysql

Hello I have a problem when I try to make a query to another database.
I have 3 tables: "MarketAgreement" and "MarketTransaction" in the koop_app (mysql) database and the "AgreementEnergia" table in the koop_app_energia database
My Models:
With the "MarketAgreement" model, WhereHas works perfectly for me, but with "AgreementEnergia" I get this error:
I have declared which database each table belongs to but still when I try to do the whereHas crossed between "AgreementEnergia" (found in the koo_app_energia database) and "MarketTransaction" (found in the koop_app database) Laravel tries to find the table in the wrong database.
How can I solve that? Thanks a lot.

I have solved it by simply changing protected $table = 'market_transactions'; for protected $table = 'koop_app.market_transactions';

For a "belongsToMany" relationship, You can do something like that:
public function your_method()
{
$database = $this->getConnection()->getDatabaseName();
return $this->belongsToMany('B', "$database.a_bs", 'a_id', 'b_id');
}
BUT: when you are modelling relationships between data, it implicitly within the same database. It is not expected to do this in two different databases
For a "Has" relationship, Eloquent does not currently support querying for relationship existence across databases. The relationships must exist within the same database (https://laravel.com/docs/8.x/eloquent-relationships#querying-relationship-existence)
PS: please do not put screenshots, but code snippets

Related

Why Laravel does not optimize model queries automatically?

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.

Laravel 5.1, Eloquent, ManyToMany with comment

I followed Jeffrey Way's ManyToMany tutorial https://laracasts.com/series/laravel-5-fundamentals/episodes/21 and I got everything and everything is working great. However I would like to add another feature to my many to many relation and that is a 'comment' that gives some more info to the object relation.
I have two models:
Article [id, title, text]
Category [id, title]
And this is a many to many relation so I create a pivot table as article_category. This table has two columns article_id and category_id and they are connected via functions in model as:
Article.php:
public function categories()
{
return $this->belongsTo('App\Category');
}
& Category.php
public function articles()
{
return $this->belongsTo('App\Article');
}
However I would like to add another field called comment to the pivot table where I could describe why this Article was added to this specific Category. Adding a column is not a problem but I don't know how to retrieve this comment from, lets say, Article instance:
$articleCategoryComment = Article::find(1)->commentFromPivotTable;
I could always define another oneToMany relation, and create another table to save the comment with fields [artice_id,category_id,comment] but I am wondering is there a better/simpler way.
Also, any good resource on database structuring will be greatly appreciated. I would prefere bunch of examples on how to do stuff right way in MySQL but a book that explains things from scratch is also good recommendation. However, at the moment I won't have time to go too deep but it will be bookmarked for future reading.
Thanks!
You should be using belongsToMany on a Many to Many relationship. If you want additional columns on your pivot table, using the withPivot() method. From the docs:
return $this->belongsToMany('App\Role')->withPivot('column1', 'column2');
You'll find more info here: http://laravel.com/docs/master/eloquent-relationships#many-to-many

Laravel 4 relationships

I can't find a way to define relationships. Here is my previous question, you can review it to find what I'm looking for: PHP/Laravel/Bootsrap fetch data
I've defined the relationship for skills like this:
public function skills()
{
return $this->hasMany('Skill', 'player_skills', 'skillid', 'player_id');
}
Still getting the Undefined property: Illuminate\Database\Eloquent\Collection::$players error pointing at $skills->players as $player.
First of all, with the hasMany function, you are setting up a one-to-many relationship, but it looks like you are sending it the name of a pivot table as a parameter, which would only be required for a many-to-many.
I'm going to assume you actually want a many to many relationship and your keys are correct (they are in the question you linked, but skillid is different here, so let's go with skill_id)
Also, the way you are looping through the data is wrong. $players->skills doesn't make any sense because $players doesn't have skills, but $player does.
Here is what I have so far... http://paste.laravel.com/Jp8
With the newest information you have given, I think there might be some confusion about how a many-to-many relationship works in Laravel. Please check this out when you get a chance, it should be pretty close to what you are looking for... http://paste.laravel.com/JqV

PHP Laravel 4 - Database table "as"

I am creating a backend interface for a site made in Joomla. I am using Laravel to create the backend. Joomla has table prefixes and tend to have long table names there are some of which around 25 characters.
My issue is, I have looked around to see if that you can alias table names (so I don't have to type the table name all the time in the join statement) by I don't seem to be able to find a method for this in the query object.
ModelName::getTable(); // Happens to be a static method.
Any helper much appreciated.
As answered in that StackOverflow question:
How to alias a table in Laravel Eloquent queries (or using Query Builder)?
you can either use aliases while building your query
$users = DB::table('really_long_table_name AS t')
->select('t.id AS uid')
->get();
or define the alias in your Eloquent model
protected $table = 'really_long_table_name AS short_name';

Limiting results of System.Data.Linq.Table<T>

I am trying to inherit from my generated datacontext in LinqToSQL - something like this
public class myContext : dbDataContext {
public System.Data.Linq.Table<User>() Users {
return (from x in base.Users() where x.DeletedOn.HasValue == false select x);
}
}
But my Linq statement returns IQueryable which cannot cast to Table - does anyone know a way to limit the contents of a Linq.Table - I am trying to be certain that anywhere my Users table is accessed, it doesn't return those marked deleted. Perhaps I am going about this all wrong - any suggestions would be greatly appreciated.
Hal
Another approach would to be use views..
CREATE VIEW ActiveUsers as SELECT * FROM Users WHERE IsDeleted = 0
As far as linq to sql is concerned, that is just the same as a table. For any table that you needed the DeletedOn filtering, just create a view that uses the filter and use that in place of the table in your data context.
You could use discriminator column inheritance on the table, ie. a DeletedUsers table and ActiveUsers table where the discriminator column says which goes to which. Then in your code, just reference the Users.OfType ActiveUsers, which will never include anything deleted.
As a side note, how the heck do you do this with markdown?
Users.OfType<ActiveUsers>
I can get it in code, but not inline
Encapsulate your DataContext so that developers don't use Table in their queries. I have an 'All' property on my repositories that does a similar filtering to what you need. So then queries are like:
from item in All
where ...
select item
and all might be:
public IQueryable<T> All
{
get { return MyDataContext.GetTable<T>.Where(entity => !entity.DeletedOn.HasValue); }
}
You can use a stored procedure that returns all the mapped columns in the table for all the records that are not marked deleted, then map the LINQ to SQL class to the stored procedure's results. I think you just drag-drop the stored proc in Server Explorer on to the class in the LINQ to SQL designer.
What I did in this circumstance is I created a repository class that passes back IQueryable but basically is just
from t in _db.Table
select t;
this is usually referenced by tableRepository.GetAllXXX(); but you could have a tableRepository.GetAllNonDeletedXXX(); that puts in that preliminary where clause to take out the deleted rows. This would allow you to get back the deleted ones, the undeleted ones and all rows using different methods.
Perhaps my comment to Keven sheffield's response may shed some light on what I am trying to accomplish:
I have a similar repository for most
of my data access, but I am trying to
be able to traverse my relationships
and maintain the DeletedOn logic,
without actually calling any
additional methods. The objects are
interrogated (spelling fixed) by a StringTemplate
processor which can't call methods
(just props/fields).
I will ultimately need this DeletedOn filtering for all of the tables in my application. The inherited class solution from Scott Nichols should work (although I will need to derive a class and relationships for around 30 tables - ouch), although I need to figure out how to check for a null value in my Derived Class Discriminator Value property.
I may just end up extended all my classes specifically for the StringTemplate processing, explicitly adding properties for the relationships I need, I would just love to be able to throw StringTemplate a [user] and have it walk through everything.
There are a couple of views we use in associations and they still appear just like any other relationship. We did need to add the associations manually. The only thing I can think to suggest is to take a look at the properties and decorated attributes generated for those classes and associations.
Add a couple tables that have the same relationship and compare those to the view that isn't showing up.
Also, sometimes the refresh on the server explorer connection doesn't seem to work correctly and the entities aren't created correctly initially, unless we remove them from the designer, close the project, then reopen the project and add them again from the server explorer. This is assuming you are using Visual Studio 2008 with the linq to sql .dbml designer.
I found the problem that I had with the relationships/associations not showing in the views. It seems that you have to go through each class in the dbml and set a primary key for views as it is unable to extract that information from the schema. I am in the process of setting the primary keys now and am planning to go the view route to isolate only non-deleted items.
Thanks and I will update more later.