How do the "has" and "whereHas" methods work in Eloquent? - laravel

I have a relationship defined between users and permisson, a user can have many permissions.
When I use "with" I get the data normally.
$user->with('permisson')->get();
I get the user with their permissions.
When I use "has" it only returns the user.
$user->has('permission')->get();
For what I've read, I should get the permissons if the user contains at least one permission.
I am using Postgres driver.

with is used for eager loading a relationship. You'd use it to fetch all of the specified relationships for each model (individually, or in a collection).
has is for using the relationship as a constraint or filter. As you said, using something like has('permission') will add a constraint to the query that says "only get Users that have at least one permission". This does not automatically load the relations like with(), it only creates the constraint.
You can combine the two if you want to take advantage of both the constraint and eager loading the results.
User::has('permission')->with('permission')->get();
Seems a bit redundant, I know.

From laravel docs:
If you wish to limit your results based on the existence of a relationship, you should use the has method. For example, if you want to retrieve all blog posts taht have at least one comment, yo may pass the name of the relationship to the has and orHas methods:
$posts = Post::has('comments')->get();
So, when you use the has method, it will not return the relationship, but just the post models which has at least one comment.
The whereHas and orWhereHas methods allows you to add customized constraints to a relationship constraint, such as checking the content of a comment (using the laravel example):
$posts = App\Post::whereHas('comments', function (Builder $query) {
$query->where('content', 'like', 'foo%');
})->get();
When you use the with method, you are loading the realtionship with the model:
$user->with('permission')->get()
This code will load the user and the permission relationship, while this one
$user->has('permission')->get();
will load the user who has some permission.
Hope it helps.

Related

How to use Query Builder to make a relation in an array? Laravel

I would like to make a relation with query builder... I have three tables, and I would like to join the tables for work with the function.. I'm working in a model.. not in a controller
This is my function
public function map($contactabilidad): array
{
$relation = DB::table('tbl_lista_contactabilidad')
->join('tbl_equipo_postventaatcs', 'tbl_equipo_postventaatcs.id', '=', 'tbl_lista_contactabilidad.postventaatc_id')
->join('users', 'users.id', '=', 'tbl_equipo_postventaatcs.asesor_id')
->get();
return [
$contactabilidad->$relation->name,
$contactabilidad->postventaatc_id,
$contactabilidad->rif,
$contactabilidad->razon_social,
$contactabilidad->fecha_contacto,
$contactabilidad->persona_contacto,
$contactabilidad->correo_contacto,
$contactabilidad->numero_contacto,
$contactabilidad->celular_contacto,
$contactabilidad->comentarios,
$contactabilidad->contactado,
$contactabilidad->respuesta->respuesta
];
}
Query\Builder is best thought of as the primary tool used by Eloquent, but is, nontheless, a completely different package. Query\Builder's purpose is to decouple SQL syntax from the logic that feeds into it, whereas Eloquent's purpose is to decouple that logic from table structures and relationships. So only Eloquent supports Model and Relation classes, Query\Builder does not. And what you're asking for has to do with Relations, so in short, you're kind of barking up the wrong tree.
By the way, I'm differentiating 'Query\Builder' here because Eloquent also has its own wrapper for that class called Eloquent\Builder that shares most of the same syntax. For better or for worse, Eloquent attempts to allow the developer to interact with it in a way that's familiar; not having to track a new set of method names even if you've been seamlessly dropped out of Eloquent and into a Query\Builder object via a magic __call method. It also does something similar regarding Eloquent\Collections vs. Support\Collections. But that can make things very confusing at first, because you have to just kind of know what package you're talking to.
So, to answer your question...
Build a Model class for each of your three tables
Apply relationship methods to each one to pre-configure the model with an awareness of your foreign keys
Call on them using lazy or eager-loading
Something else to note is that with() does not ask Eloquent to perform a JOIN. All it does is run the parent query, extract the key values from the result, run the child query using them in an IN() statement, and marrying the results together afterwards. That's what results in nested results. Speaking from experience, it's kind of a mess generating true JOIN statements off Model Relations and keeping the table aliases unique, so it makes sense this package just skips trying to do that (except with pivot tables on many-to-many relations). This also has the added benefit though, that your related tables don't need to live in the same database. A Query\Builder join() on the other hand, as you have there, would present all fields for all tables at the top-level.

Which relation to use in Laravel?

Which relation to use in Laravel to bind two table through third?
When Doctors can be assigned to some Centers. The intermediate table will be as:
doctor_id | center_id
How to create model in Laravel for this case?
You don't need a model for the intermediate table, simply use attach
Example:
$center = Center::create();
$doctor = Doctor::find(1);
$doctor->centers()->attach($doctor->id);
This is a very simple example but should give you the idea, of how to approach it.
All of it of course requires you have set up your Center and Doctor model with the correct many to many relations
Doctor.php model:
public function centers()
{
return $this->belongsToMany(Doctor::class);
}
See the documentation, for more information.
You could obviously create a model called DoctorsCenter and create it manually by doing this, whenever you want to attach a relation.
DoctorsCenter::create(['center_id' => $center->id, 'doctor_id' => $doctor->id]);
I don't see any good reason for doing this, and would not recommend it.
You can use hasMany or belongsTo relationship of Laravel.
See the laravel documentation, for more information

How to use eager loading with custom query builder in Laravel

I am new to Laravel 5.4 and working on some query manipulation. Now I have created an query using query builder which is shown below:
$view = DB::table('blocks')
->leftjoin('programmes', 'blocks.programme_id', '=', 'programmes.id')
->select('blocks.id', 'blocks.programme_id', 'blocks.name', 'blocks.colour', 'blocks.year', 'programmes.title AS programme');
I have two more table "dates" and "modules". Each dates as well as module belongs to blocks.
Now I want to fetch all blocks with programmes, dates and modules. I know i can use with() method to get all of these. But as per my knowledge on Laravel, I can use with() method only if I have model file of each table and have relationship between them.
But do not want to use model and define relationship between them. I just want to know How can I get block data with programmes, dates and modules without creating model and defining relationship betwen them in model? Is there any other way to use with() method without model?
Block dates and modules are conditional, Sometime I dont want get data of this table with block.
Please help me on this.
You can't do it automatically. Eager loading is only for Eloquent model so you cannot use it with query builder. However in most cases you can use Eloquent also for getting more complicated queries (you can also use joins when using Eloquent) so you will be able to use eager loading.
But if you don't want to use Eloquent at all, obviously you will need to create some custom mechanism for eager loading.

Laravel relations with composite, non-standard foreign keys

I unfortunately need to import data from a third-party vendor and use their non-standard database schema with my laravel project. In addition, I need to store multiple "firms," each with their own set of users in my database.
I'm trying to figure out the best way (if it can be done) to use Eloquent to handle the relationships between these tables. So for instance, with my table structure like this:
BmPerson
'id',
'firmId',
'personId'
BmCoverage
'id',
'firmId',
'personId',
'securityId'
BmSecurity
'id',
'firmId',
'securityId'
... for instance, I need to associate a "BmPerson" with many "BmSecurity" through the "BmCoverage" table.
But I need to somehow use composite keys, because I am storing multiple "firms" in each table (per the 3rd party vendor's database schema).
One approach I've used so far is scoping, e.g.: for my BmCoverage model:
public function scopeFromFirm($query,$firmId){
return $query->where('firmId','=',$firmId);//->where('personId','=',$personId);}
public function scopeFromPerson($query,$personId){
return $query->where('personId','=',$personId);//->where('personId','=',$personId);}
Then I can retrieve the coverage list for an individual person, but I still need to somehow be able to associate the "BmCoverage" with the "BmSecurities." I suppose I could just add a scope the BmSecurities class too, but it would be nicer to just use Eloquent.
Has anyone come up with a good way to use composite keys in laravel model relationships, or should I just stick with the scoping method?
There is a package here that seems to be perfect for your case:
Compoships offers the ability to specify relationships based on two
(or more) columns in Laravel 5's Eloquent. The need to match multiple
columns in the definition of an Eloquent relationship often arises
when working with third party or pre existing schema/database.
You would use it like this:
class BmPerson extends Model
{
use \Awobaz\Compoships\Compoships;
public function bmCoverages()
{
return $this->hasMany('App\BmCoverage', ['firmId', 'personId'], ['firmId', 'personId']);
}
}
If every BmSecurity belongs to exactly one BmCoverage, and every BmCoverage belongs to exactly one BmPerson its probably easier to replace 'firmId', 'personId' with bmperson_id in BmCoverage DB; and 'firmId', 'securityId' with bmcoverage_id in BmSecurity. Then you can use default hasMany relations with one key.
Everything you need for this can be found here https://laravel.com/docs/5.2/eloquent-relationships
You can easily define which cols sohuld be the referenced key.
Example:
public function bmCoverages() {
return $this->hasMany('App\BmCoverage', 'firmId', 'id');
}
This would probably belong to your App\Firm or whatever it is called.
In general the hasMany relations looks like this
return $this->hasMany('App\Comment', 'foreign_key', 'local_key');
As you can see you can specify the keys.
As the others have said, you need to use the HasMany and HasManyThrough relationship.
Here from your table definitions, you simply need access to:
Person->BmCoverage(s)
Person->BmSecurity(s) of an individual.
What I think is the major problem here is linking the BmSecurity with BmCoverage as apparently there's no coverage_id per BmSecurity but rather, a composite mapping through firmId and securityId.
In this case, Laravel does not officially support composite keys unfortunately, although you could use a trait like this... but you could also achieve the same with some tricky hasMany.
i.e. on BmCoverage
$this->hasMany('BmSecurity', 'securityId', 'securityId')
->andWhere('firmId', '=', $this->firmId);
Same applies for BmSecurity from BmPerson using HasManyThrough.
Hope that helps.
read laravel hasManyThrough relationship . it will help you to write this query more easily
https://laravel.com/docs/5.1/eloquent-relationships#has-many-through

How can I return Many to One relation data joined with One to One data?

I have a question concerning Laravel schema.
Was that a bad idea to create an additional one-to-one table user_description, to store additional user data? Here is my schema:
For example now I don't know how to use eager loading to get user_photos if at first I retrieve topics' data.
Here is the code I use to get the last topics with user data:
return Topic::with('blog', 'user')->orderBy('topics.id', 'DESC')->get();
assuming your User-Model has a relation description() I suppose this does what you want.
return Topic::with('blog', 'user', 'user.description')->orderBy('topics.id', 'DESC')->get();
the concept is called sth. like eager loading of nested-relationships and is documented at the bottom of the eager loading documentation of laravel

Resources