eloquent refer to a column of a related a model - eloquent

I have three tables:
categories
id, title
products
id, name
categories_products
id, category_id, product_id
I have also setup the according models and relationships (both have belongsToMany of the other)
Now I want to get all products belonging to a category
Category::where('title','Electronics')->first()->products()->limit(10)->get(['products.name']);
which works fine, but I also want to include the category title for each product as well:
Category::where('title','Electronics')->first()->products()->limit(10)->get(['products.name','category.title']);
However it returns: Column not found category.title
I thought that the relation would take care of it.
EDIT: Models -->
Category:
class Category extends Model
{
protected $fillable = array('title');
public function products()
{
return $this->belongsToMany('Product', 'categories_products', 'category_id', 'product_id');
}
}
class Product extends Model
{
protected $fillable = array('name');
public function categories()
{
return $this->belongsToMany('Category', 'categories_products', 'product_id', 'category_id');
}
}

The reason you're getting the error is because get() works just like select() and because you're running the category query and then running the product query after there is no categories table to reference for the select.
Look into Eager Loading. It will help with a lot of these kinds of issues. Your query can be written as:
Product::select('id', 'name')
->with(['categories' => function($query) {
return $query->select('id', 'title');
}])
->whereHas('categories', function($query) {
return $query->where('title', 'Electronics');
})
->limit(10)
->get();
Because we are lazy loading you NEED the id column on each model so Laravel knows where to attach the relationships after the queries are run.
The with() method above will eager load the categories relationship and the whereHas() method puts a relationship constraint on the current query.
UPDATE
Similar query from Category model:
$category = Category::where('title','Electronics')
->with(['products' => function($query) {
return $query->select('id', 'name')->limit(10);
}])
->first(['id', 'title']);
Then access the products with:
$category->products

Related

Laravel eloquant belongsToMany get records belongs to relationship

I'm using many to many relationship with products and product_categories tables using product_product_category pivot table.
Product model
class Product extends Model
{
public function product_categories() {
return $this->belongsToMany(ProductCategory::class, 'product_product_category');
}
}
ProductCategory model
class ProductCategory extends Model {
public function products() {
return $this->belongsToMany(Product::class, 'product_product_category');
}
}
What I need to do is when I supply an array of categories need to get products only with these categories. This is my code
$selectedCategotries = array(1, 2);
$products = Product::with(['product_categories' => function($q) use ($selectedCategotries){
$q->whereIn('product_categories.id', $selectedCategotries);
}])->get();
But I get all the products instead. It will be a great help if you can supply a solution for me.
Finally, I found an answer with whereHas. Adding the answer for anyone who come up with the same issue.
$products = Product::whereHas('product_categories', function ($q) use ($selectedCategotries) {
$q->whereIn('product_categories.id', $selectedCategotries);
})->get();

How to update a ManyToMany relationship in Objection JS?

I am learning Objection JS. I have a manyToMany relationship on an accounts and roles table that are related through an accounts_roles table. I was wondering if there was a way to update account roles on the account model by doing something like:
AccountOne.addRoles([roleId])
AccountOne.removeRoles([roleId])
AccountOne.updateRoles([roleId])
AccountOne.deleteRoles([roleId])
I searched online and went through the official objection documentation. So far I can do a GraphInsert, on my account model with a role object "x", and that creates a new role "x" with a relationship correctly defined in accounts_roles. But that always creates a new role. I would like to add and remove relationships between existing accounts and roles. Any help would be much appreciated.
You can use relate and unrelate to connect two rows together in a many-to-many relationship. Obviously, for this to work, you have to have your Models' relationMappings set up correctly. I have put a pair of example models too. It's critical that you match your key of your relationMappings to what you put in your $relatedQuery method to call relate/unrelate on.
Example Models
//Person and Movie Models
const { Model } = require('objection');
class Person extends Model {
static tableName = 'persons';
static get relationMappings() {
return {
movies: {
relation: Model.ManyToManyRelation,
modelClass: Movie,
join: {
from: 'persons.id',
through: {
from: 'persons_movies.person_id',
to: 'persons_movies.movie_id'
},
to: 'movies.id'
}
}
};
}
}
class Movie extends Model {
static tableName = 'movies';
static get relationMappings() {
return {
persons: {
relation: Model.ManyToManyRelation,
modelClass: Person,
join: {
from: 'movies.id',
through: {
from: 'persons_movies.movie_id',
to: 'persons_movies.person_id'
},
to: 'persons.id'
}
}
};
}
}
Relate Example
In the case of a many-to-many relation, creates a join row to the join table.
const person = await Person
.query()
.findById(123);
const numRelatedRows = await person
.$relatedQuery('movies')
.relate(50);
// movie with ID 50 is now related to person with ID 123
// this happened by creating a new row in the linking table for Movies <--> Person
Unrelate Example
For ManyToMany relations this deletes the join row from the join table.
const person = await Person
.query()
.findById(123)
const numUnrelatedRows = await person
.$relatedQuery('movies')
.unrelate()
.where('id', 50);
// movie with ID 50 is now unrelated to person with ID 123
// this happened by delete an existing row in the linking table for Movies <--> Person

Eloquent ORM One to many query is not working

I am working with laravel 5.4. I have created countries and states table.
countries table looks like below
And my states table is :
Here, I have written join query as shown in below. It works perfect.
$state = DB::table($this->tbl_states)
->join($this->tbl_countries, $this->tbl_countries.'.id', '=', $this->tbl_states.'.country_id')
->select($this->tbl_states.'.*', $this->tbl_countries.'.name as country_name')
->whereNull($this->tbl_states.'.deleted_at')
->paginate(10)
But, Instead of writing this query I want to use Eloquent ORM so what query should I have to write?
In Country model I have create function that looks like below :
public function states()
{
return $this->hasMany('state');
}
And in State model I have write function that looks like below :
public function country()
{
return $this->hasOne('country');
}
In Country model try this:
public function states()
{
return $this->hasMany(App\State::class);
}
And in State model:
public function country()
{
return $this->belongsTo(App\Country::class);
}
And then $country->states will give you all states of this country. As well as $state->country will return state's country.
Official Docs: Eloquent: Relationships

Get Discriminator in Linq Select results

I'm trying to make a SQL Query with LinqToEntities which will be as efficient as possible so I would like to avoid iterating after every record I get in results to get a new corrected collection. I'm trying to create a ready collection to send as a result in API just with this one query.
I'm trying to get a discriminator value though with inherited models.
My models are somewhat like these:
public abstract class DbEntity
{
[NotMapped]
public string DiscriminatorValue
{
get
{
return ObjectContext.GetObjectType(GetType()).Name;
}
}
}
public class Foo : DbEntity {
}
public class Bar: DbEntity {
}
This model will create a table in database which will have a column called Discriminator. To get the discriminator value I used my custom DiscriminatorValue property but it won't work in queries like this:
var events = context.MyTable.Select(t => new {
Discriminator = t.DiscriminatorValue
}).ToList();
This below one will obviously work, but it will be much slower imho:
var events = context.MyTable.ToList().Select(t => new {
Discriminator = t.DiscriminatorValue
}).ToList();
Is it possible to get Discriminator value without having to write my custom SQL query myself (like real sql which I know is possible too).

How to get result by joining two collections (tables) [Cakephp/MongoDB]

I am using https://github.com/ichikaway/cakephp-mongodb.git plugin for accessing mongodb datasource.
I have two Models: Teachers and Subject. I want joint find result on Teacher and Subject.
Here are my two models:
Teacher:
<?php
class Teacher extends AppModel {
public $actsAs = array('Containable');
public $hasOne = array('Subject');
public $primaryKey = '_id';
var $mongoSchema = array(
'name'=>array('type'=>'string'),
'age'=>array('type'=>'string'),
'subjectid'=>array('type'=>'string'),
'created'=>array('type'=>'datetime'),
'modified'=>array('type'=>'datetime'),
);
Subject:
<?php
class Subject extends Model {
public $actsAs = array('Containable');
public $belongsTo= array('Teacher');
public $primaryKey = '_id';
var $mongoSchema = array(
'name'=>array('type'=>'String'),
'code'=>array('type'=>'String'),
'created'=>array('type'=>'datetime'),
'modified'=>array('type'=>'datetime')
);
In Teachers Controller to get joint result, I did:
$results = $this->Teacher->find('all',array('contain'=>array('Subject')));
$this->set('results', $results);
But I am not getting any result from Subjects Collections.
Here is what I am getting:
array(5) {
[0]=>
array(1) {
["Teacher"]=>
array(7) {
["_id"]=>
string(24) "52e63d98aca7b9ca2f09d869"
["name"]=>
string(13) "Jon Doe"
["age"]=>
string(2) "51"
["subjectid"]=>
string(24) "52e63c0faca7b9272c09d869"
["modified"]=>
object(MongoDate)#78 (2) {
["sec"]=>
int(1390820760)
["usec"]=>
int(392000)
}
["created"]=>
object(MongoDate)#79 (2) {
["sec"]=>
int(1390820760)
["usec"]=>
int(392000)
}
}
}
I am a cakephp/mongoDB rookie only, Please guide me to get the desired result.
Edit: I read that mongoDb don't support Join Operation. Then How to manually do it? I mean I can write two find query on each Model, then how to combine both array and set it to view?
As you said, MongoDB does not support joins. Documents in query results come directly from the collection being queried.
In this case, I imagine you would query for Teacher documents and then iterate over all documents and query for the Subject documents by the subjectid field, storing the resulting subject document in a new property on the Teacher. I'm not familiar with this CakePHP module, but you may or may not need to wrap the subjectid string value in a MongoId object before querying. This depends on whether or not your document _id fields in MongoDB are ObjectIds or plain strings.
If Subjects only belong to a single teacher and you find that you never query for Subjects outside of the context of a Teacher, you may want to consider embedding the Subject instead of simply storing its identifier. That would remove the need to query for Subjects after the fact.