Laravel 4 querying relational relation - php

I've created 4 database tables:
Tool
ID | NAME | TOOLTYPE_ID
Tooltype
ID | NAME
Toolcategory
ID | NAME
Tool_Toolcategory
TOOL_ID | TOOLCATEGORY_ID
The models are as following:
class Tool extends Eloquent {
public function toolCategories()
{
return $this->belongsToMany('ToolCategory', 'tool_toolcategory', 'tool_id', 'toolcategory_id');
}
public function tooltype()
{
return $this->belongsTo('ToolType');
}
}
class ToolType extends Eloquent {
public function tools()
{
return $this->hasMany('Tool', 'tooltype_id');
}
}
class ToolCategory extends Eloquent {
public function tools()
{
return $this->belongsToMany('Tool', 'tool_toolcategory', 'tool_id', 'toolcategory_id');
}
}
Ok, my problem is getting toolcategories based on created tools with a specific tooltype.
Example I want all the categories related to the tools that has the type "Software". I am kinda lost, i have looked at trying to use scope in my toolcategory model, doing something like this:
public function scopeHasType($query, $type)
{
return $query->whereHas('tools.tooltype', function($q) use ($type)
{
$q->where('name', '=', $type);
})->exists();
}
which didn't really work :) then i tried this
$categories = ToolCategory::whereHas('tools.tooltype', function($query)
{
$query->where('tooltype_id', '=', 'Software');
})->get();
again no luck. So i was hoping that this makes sense to someone, and they could push me in the right direction. Thank you in advance.

Eloquent naming convention says:
ModelNames are StudlyCased & singular
modelMethods are camelCased (relation dynamic properties need this)
table_names are snake_cased & plural
table_columns are snake_cased
pivot_table is model1_model2 snake_cased singular (in alphabetical order)
That being said, Eloquent for a model ToolCategory looks for the table tool_categories.
So you need to specify table names whenever are not convention compliant (singular, not snake cased and so on):
// for example Tool model
protected $table = 'tool';
Also you need foreign keys to be passed to the relationship definition in order to let Eloquent know what to look for. However that you already did:
// ToolType model
public function tools()
{
// here Eloquent would look for tool_type_id (based on related model)
return $this->hasMany('Tool', 'tooltype_id');
}
// Tool model
public function tooltype()
{
// here Eloquent looks for tooltype_id by default (based on relation name)
return $this->belongsTo('ToolType');
}
Now, your relations are OK with 1 exception (keys wrong order):
class ToolCategory extends Eloquent {
public function tools()
{
return $this->belongsToMany('Tool', 'tool_toolcategory', 'toolcategory_id', 'tool_id');
}
Finally, whereHas doesn't work on nested relation (yet, check this: https://github.com/laravel/framework/pull/4954), so at the moment you need this:
// ToolCategory model
public function scopeHasType($query, $type)
{
return $query->whereHas('tools', function ($q) use ($type) {
$q->whereHas('tooltype', function($q) use ($type) {
// use table prefix, since this is going to be join
$q->where('tooltype.name', '=', $type);
});
});
}

Related

How to join two tables with a pivot table using Laravel?

I have these three tables:
tbl_lista_contactabilidad tbl_equipo_postventaatc users
------------------------- ----------------------- -----
id id id
usuarios_id asesor_id name
tbl_lista_contactabilidad.usuarios_id should be related with tbl_equipo_postventaatc.asesor_id. asesor_id should be the "pivot" between tbl_lista_contactabilidad.usuarios_id and users.id to make the relation.
I want to make this relation so I tried to do this relation in this way (I will put only the relation of the model)
Tbl_Lista_Contactabilidad (Model 1)
public function postventaatc(){
return $this->belongsTo('App\Models\Tbl_EquipoPostventaatc','usuarios_id');
}
Tbl_Equipo_Postventaatc (Model 2) -> This should be the pivot model
public function contactabilidad(){
return $this->hasMany('App\Models\Tbl_Lista_Contactabilidad','usuarios_id');
}
public function user(){
return $this->belongsTo('App\Models\User','asesor_id');
}
User (Model 3)
public function postventaatc(){
return $this->hasMany('App\Models\Tbl_Lista_Postventaatc','asesor_id');
}
EXAMPLE:
As you see in the image... if I relate usuarios_id with users directly I will get another name and I don't want that... I want the relation just like in the image
A pivot table is a structure used to join two separate models together with a single relationship. This is called a many-to-many relationship in Eloquent.
From what you've described, this is not the case here. Rather, it looks like a has-many-through relationship.
If I'm understanding correctly, your relationships should look like this:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Tbl_Lista_Contactabilidad extends Model {
protected $table = 'tbl_lista_contactabilidad';
public function postventaatc() {
return $this->belongsTo(Tbl_EquipoPostventaatc::class, 'usuarios_id');
}
}
class Tbl_EquipoPostventaatc extends Model {
protected $table = 'tbl_equipo_postventaatc';
public function contactabilidad() {
return $this->hasMany(Tbl_Lista_Contactabilidad::class, 'usuarios_id');
}
}
class User extends Model {
public function postventaatc() {
return $this->belongsTo(Tbl_EquipoPostventaatc::class, 'asesor_id');
}
public function contactabilidad() {
return $this->hasManyThrough(Tbl_Lista_Contactabilidad::class, Tbl_EquipoPostventaatc::class, 'asesor_id', 'usuarios_id');
}
}
Obviously this is easier for a native English speaker, but I cannot stress how much easier this would be if you were following the Laravel rules around naming your models, tables, and columns. Why does usuarios_id column relate to a table called tbl_equipo_postventaatc? Why use asesor_id instead of user_id? 🤷🏽‍♂️ Those names have nothing to do with each other, and make it hard to figure out what is going on.

Laravel 5.4 relationship between two tables and ORM request

I'm making a search bar, I have a problem in the request with ORM Laravel ...
Their is my models :
class Ville extends Model
{
public function house() {
return $this->hasMany('App\House');
}
}
class House extends Model {
public function ville() {
return $this->belongsTo('App\Ville');
}
}
House table has an "id_ville". I'm searching for the request with ORM like :
public function index()
{
$search = \Request::get('search');
$houses = house::where('ville_nom', 'like', '%'.$search.'%');
return view('home')->with('houses', $houses);
}
but I can't see anything in the template ..
Check the Docs, namely the section about Defining the Inverse Relationship https://laravel.com/docs/5.4/eloquent-relationships#defining-relationships
There, you'll see this:
Eloquent determines the default foreign key name by examining the name of the relationship method and suffixing the method name with _id. However, if the foreign key on the Phone model is not user_id, you may pass a custom key name as the second argument to the belongsTo method:
Since your key is prefixed with id instead of suffixed, you'll need to update your relationship to explicitly specify the FK.
If you are not strictly following the primary and foreign keys Eloquent conventions, you can easily specify your own:
class House extends Model {
public function ville() {
return $this->belongsTo(Ville::class, 'id_ville');
}
}
And here is a way to retrieve houses by searching for a Ville.
public function index(Request $request)
{
$houses = House::whereHas('ville', function ($query) {
$query->where('ville_nom', 'LIKE', '%'. $request->search .'%');
})->get();
return view('home')->with('houses', $houses);
}

Laravel 5.2 relationship with pivot table

my tables:
every part contain many of card.
every card belong to many of part.
now,using laravel eloquent model how can fetch all card for a part without add more column to database
You need to define your relationships like below:
class Part extends Model
{
public function cards()
{
return $this->belongsToMany('App\Cards', 'user_cards');
}
}
Then you can fetch all the cards for a part like below:
$cards = Part::with('cards')->find($part_id);
Part::whereHas('cards', function($query) use ($cardId){
$query->where('id', $cardId);
})->get();
And your model relation should contain like this, for Part.php
public function cards(){
return $this->belongsToMany('App\Card');
}
And for Card.php
public function parts(){
return $this->belongsToMany('App\Part');
}

How to set Eloquent relationship belongsTo THROUGH another model in Laravel?

I have a model Listing that inherits through its belongsTo('Model') relationship should inherently belong to the Manufacturer that its corresponding Model belongs to.
Here's from my Listing model:
public function model()
{
return $this->belongsTo('Model', 'model_id');
}
public function manufacturer()
{
return $this->belongsTo('Manufacturer', 'models.manufacturer_id');
/*
$manufacturer_id = $this->model->manufacturer_id;
return Manufacturer::find($manufacturer_id)->name;*/
}
and my Manufacturer model:
public function listings()
{
return $this->hasManyThrough('Listing', 'Model', 'manufacturer_id', 'model_id');
}
public function models()
{
return $this->hasMany('Model', 'manufacturer_id');
}
I am able to echo $listing->model->name in a view, but not $listing->manufacturer->name. That throws an error. I tried the commented out 2 lines in the Listing model just to get the effect so then I could echo $listing->manufacturer() and that would work, but that doesn't properly establish their relationship. How do I do this? Thanks.
Revised Listing model (thanks to answerer):
public function model()
{
return $this->belongsTo('Model', 'model_id');
}
public function manufacturer()
{
return $this->belongsTo('Model', 'model_id')
->join('manufacturers', 'manufacturers.id', '=', 'models.manufacturer_id');
}
I found a solution, but it's not extremely straight forward. I've posted it below, but I posted what I think is the better solution first.
You shouldn't be able to access manufacturer directly from the listing, since manufacturer applies to the Model only. Though you can eager-load the manufacturer relationships from the listing object, see below.
class Listing extends Eloquent
{
public function model()
{
return $this->belongsTo('Model', 'model_id');
}
}
class Model extends Eloquent
{
public function manufacturer()
{
return $this->belongsTo('manufacturer');
}
}
class Manufacturer extends Eloquent
{
}
$listings = Listing::with('model.manufacturer')->all();
foreach($listings as $listing) {
echo $listing->model->name . ' by ' . $listing->model->manufacturer->name;
}
It took a bit of finagling, to get your requested solution working. The solution looks like this:
public function manufacturer()
{
$instance = new Manufacturer();
$instance->setTable('models');
$query = $instance->newQuery();
return (new BelongsTo($query, $this, 'model_id', $instance->getKeyName(), 'manufacturer'))
->join('manufacturers', 'manufacturers.id', '=', 'models.manufacturer_id')
->select(DB::raw('manufacturers.*'));
}
I started off by working with the query and building the response from that. The query I was looking to create was something along the lines of:
SELECT * FROM manufacturers ma
JOIN models m on m.manufacturer_id = ma.id
WHERE m.id in (?)
The query that would be normally created by doing return $this->belongsTo('Manufacturer');
select * from `manufacturers` where `manufacturers`.`id` in (?)
The ? would be replaced by the value of manufacturer_id columns from the listings table. This column doesn't exist, so a single 0 would be inserted and you'd never return a manufacturer.
In the query I wanted to recreate I was constraining by models.id. I could easily access that value in my relationship by defining the foreign key. So the relationship became
return $this->belongsTo('Manufacturer', 'model_id');
This produces the same query as it did before, but populates the ? with the model_ids. So this returns results, but generally incorrect results. Then I aimed to change the base table that I was selecting from. This value is derived from the model, so I changed the passed in model to Model.
return $this->belongsTo('Model', 'model_id');
We've now mimic the model relationship, so that's great I hadn't really got anywhere. But at least now, I could make the join to the manufacturers table. So again I updated the relationship:
return $this->belongsTo('Model', 'model_id')
->join('manufacturers', 'manufacturers.id', '=', 'models.manufacturer_id');
This got us one step closer, generating the following query:
select * from `models`
inner join `manufacturers` on `manufacturers`.`id` = `models`.`manufacturer_id`
where `models`.`id` in (?)
From here, I wanted to limit the columns I was querying for to just the manufacturer columns, to do this I added the select specification. This brought the relationship to:
return $this->belongsTo('Model', 'model_id')
->join('manufacturers', 'manufacturers.id', '=', 'models.manufacturer_id')
->select(DB::raw('manufacturers.*'));
And got the query to
select manufacturers.* from `models`
inner join `manufacturers` on `manufacturers`.`id` = `models`.`manufacturer_id`
where `models`.`id` in (?)
Now we have a 100% valid query, but the objects being returned from the relationship are of type Model not Manufacturer. And that's where the last bit of trickery came in. I needed to return a Manufacturer, but wanted it to constrain by themodelstable in the where clause. I created a new instance of Manufacturer and set the table tomodels` and manually create the relationship.
It is important to note, that saving will not work.
$listing = Listing::find(1);
$listing->manufacturer()->associate(Manufacturer::create([]));
$listing->save();
This will create a new Manufacturer and then update listings.model_id to the new manufacturer's id.
I guess that this could help, it helped me:
class Car extends Model
{
public function mechanical()
{
return $this->belongsTo(Mechanical::class);
}
}
class CarPiece extends Model
{
public function car()
{
return $this->belongsTo(Car::class);
}
public function mechanical()
{
return $this->car->mechanical();
}
}
At least, it was this need that made me think of the existence of a belongsToThrough
You can do something like this (Student Group -> Users -> Poll results):
// poll result
public function studentGroup(): HasOneDeep
{
return $this->hasOneDeepFromRelations($this->user(), (new User())->studentGroup());
}

Laravel Eloquent Joins

I have below query in core php:
SELECT DISTINCT device_tocken FROM push_details JOIN users ON users.id=push_details.user_id
I have to integrate it in laravel 4
Application already have User extends Eloquent class
I created Push_details class as below
class Push_details extends Eloquent {
public $table = 'push_details';
public function User() {
return $this->hasMany('\User','id');
}
}
Table : users
Primary key : id
Table: push_details
Primary key: id
Foreign key: user_id belongsTo('users.id');
But i m not able to get expected result.
One more thing i didn't write anything in User's model yet.
Only way to join table is.. to join it, as Eloquent relations don't work using joins but separate queries with WHERE IN clauses. So this will do:
DB::table('push_details')
->select('device_tocken')
->distinct()
->join('users','users.id','=','push_details.user_id')
->get();
Above will return array of stdObject's so or if you need Eloquent Collection with Eloquent models as a result replace DB::table('push_details')->select... with PushDetails::select...
Now, correct your relations, as they are wrong:
// PushDetails model (as previously stated, I suggest renaming it to StudlyCase)
public function user() {
return $this->belongsTo('\User','user_id'); // user_id is may be omitted here
}
// User model
public function pushDetails() {
return $this->hasMany('\PushDetails','user_id'); // user_id is may be omitted here as well
}
In your User model, you need to link back to the PushDetails model, like so
class User extends Eloquent {
public function push_details() {
return $this->belongsTo('PushDetails');
}
}
Use CamelCase for Class names, because laravel has several functions, in which CamelCase are changed to snake_case
Change
public function User() {
return $this->hasMany('\User','id');
}
to
public function users() {
return $this->hasMany('User');
}
See the docs 'Eloquent ORM' for more...

Categories