I have two models in which I need to relate to, a Users model and a Prices model. In my Prices model there is a JSON object which holds an ID of a user and I was wondering if I could relate to my Prices table using the ID which is in the Prices model?
I know you could use an getAttribute and then return the user like that, but I was wondering if there is a $this->hasOne() method you could use?
e.g.
JSON
{user_id: 1, other_values:"in the object"}
Prices Model
class Prices extends Model {
/* Prices has the column 'object' which has the JSON object above */
protected $casts = ['object' => 'array'];
public function user(){
return $this->hasOne("App\User", $this->object->user_id, "id"); /* ! Example ! */
}
}
I created a package with JSON relationships: https://github.com/staudenmeir/eloquent-json-relations
Since the foreign key is in the Prices model, you should use a BelongsTo relationship:
class Prices extends Model {
use \Staudenmeir\EloquentJsonRelations\HasJsonRelationships;
protected $casts = ['object' => 'array'];
public function user() {
return $this->belongsTo(User::class, 'object->user_id');
}
}
class User extends Model {
use \Staudenmeir\EloquentJsonRelations\HasJsonRelationships;
public function prices() {
return $this->hasMany(Prices::class, 'object->user_id');
}
}
Related
I have two models Product and Images. I changed the route key name on the product model to use the slug field and i'm now unable to load the hasMany relationship with the Image Model
Here is the Product Model
class Product extends Model
{
protected array $with = ['images'];
public function getKeyName()
{
return 'slug';
}
protected array $guarded = [];
public function images() : HasMany
{
return $this->hasMany(Image::class, 'product_id');
}
}
and the Image model
class Image extends Model
{
protected array $guarded = [];
public function image() : BelongsTo
{
return $this->belongsTo(Product::class);
}
}
so when I try
Product::first()->images
it just returns an empty collection
but without overriding the getKeyName() method, everything works fine
getKeyName() will get the primary key for the model. it supports to return id, after you change it to slug, it will return slug
And hasManyHere's the source code ;
The third parameter LocalKey will use getKeyName() when it's empty.
If you still want to use hasMany, you need to pass the third parameter like this:
public function images()
{
return $this->hasMany(Image::class, 'product_id', 'id');
}
This will convert the Eloquent query to database query, which will take the right local key products.id.
These are my tables many-to-many:
products and suppliers, however I need to relate the pivot(product_supplier) to a table called payment_supplier.
Product model
public function suppliers(){
return $this->belongsToMany('App\Supplier');
}
Supplier model
public function products(){
return $this->belongsToMany('App\Product');
}
but I need to relate pivot product_supplier to payment_supplier table just like described on the diagram
In this case, you could use a pivot model.
# Product Model
public function suppliers() {
return $this->belongsToMany(Supplier::class)->using(ProductSupplier::class);
}
# Supplier Model
public function products(){
return $this->belongsToMany(Product::class)->using(ProductSupplier::class);
}
# ProductSupplier Pivot Model
<?php
namespace App;
use Illuminate\Database\Eloquent\Relations\Pivot;
class ProductSupplier extends Pivot
{
public function payment_supplier()
{
return $this->hasMany(PaymentSupplier::class);
}
}
However, doing it like this has a big problem: You CANNOT eager load a pivot's relationships. Not without an override (or a package).
The other way to go about it is using hasManyThrough
# Product Model
public function suppliers()
{
return $this->belongsToMany(Supplier::class)->using(ProductSupplier::class);
}
public function payment_suppliers()
{
return $this->hasManyThrough(PaymentSupplier::class, ProductSupplier::class);
}
# Supplier Model
public function products()
{
return $this->belongsToMany(Product::class)->using(ProductSupplier::class);
}
public function payment_suppliers()
{
return $this->hasManyThrough(PaymentSupplier::class, ProductSupplier::class);
}
This gives you every PaymentSupplier for a single Supplier/Product, so you'll need to apply some kind of filtering.
I have a table named reports in which i have two columns. One is reported_post_id and the other is reporter_id. In reporter_id the value can be more than 1 and it's json. I want to make a relationship between the unique reported_post_id with the many reporter_id which is already saved in column as json data.
public function reported() {
return $this->hasMany('App\Models\Repoters', 'reported_post_id');
}
public function reporters() {
return $this->belongsTo('App\Model\Repoters', 'reporter_id');
}
$column = [\DB::raw("*"), \DB::raw("id as postable_id"),\DB::raw("report_type as postable_type")];
$data = Repoters::select($column)->with("repos")->orderBy('id', 'DESC')->paginate(50)->unique("reported_id");
I want that to show all data of reported_post_id with reporter_id.
You could make use of the eloquent-json-relations package.
Install it:
composer require staudenmeir/eloquent-json-relations:"^1.1"
Then in your User model (or the relevant one):
class Repoters extends Model
{
use \Staudenmeir\EloquentJsonRelations\HasJsonRelationships;
public function reported() // or the name that you want
{
return $this->hasManyJson('App\Model\Repoters', 'reporter_id');
} // ^^^^^^^^^^^the json column.
public function reporter() // or the name that you want
{
return $this->belongsTo('App\Model\Repoters', 'reported_post_id');
}
}
From the package documentation:
Many-To-Many Relationships
This package also introduces two new relationship types:
BelongsToJson and HasManyJson
On Laravel 5.6.25+, you can use them to implement many-to-many
relationships with JSON arrays.
In this example, User has a BelongsToMany relationship with
Role. There is no pivot table, but the foreign keys are stored as an
array in a JSON field (users.options):
Array of IDs
By default, the relationship stores pivot records as an array of IDs:
class User extends Model
{
use \Staudenmeir\EloquentJsonRelations\HasJsonRelationships;
protected $casts = [
'options' => 'json',
];
public function roles()
{
return $this->belongsToJson('App\Role', 'options->role_ids');
}
}
class Role extends Model
{
use \Staudenmeir\EloquentJsonRelations\HasJsonRelationships;
public function users()
{
return $this->hasManyJson('App\User', 'options->role_ids');
}
}
I have three tables:
collections which has id, name
genre_collection which has id, genre_id, collection_id
genres which has id, name
I want to retrieve data from collections with generes.
Collections Model
class Collections extends Model{
public function genres(){
return $this->hasMany('App\Models\GenreCollectionRelationships', 'genre_id' , 'id');
}
}
generic_collection
class GenreCollectionRelationships extends Model{
public function genre(){
return $this->hasOne('App\Models\Genres', 'id', 'genre_id');
}
}
Search Controller
class SearchController extends Controller{
$collection->genres;
foreach($collection->genres as $item){
$item->genre;
}
}
This code is working fine. And the output is
Actual
"genres": [{
"id": 1,
"genre_id": 1,
"collection_id": 1,
"created_at": "2019-02-07 17:13:36",
"updated_at": "2019-02-07 17:13:36",
"genre": {
"name": "Action",
"meta": null
}
}]
Is there any way i could directly get the output as shown below
Expected
"genres": [ {
"name": "Action",
"meta": null
}]
I tried hasManyThrough, belongsToMany but nothing worked out.
Note. I am on laravel 5.7
Thanks in advance.
You could build your own query to achieve what you are looking for. Try this:
$collection = Collection
::join('genres', 'genre.id', '=', 'collections.genre_id')
->select('collections.*', 'genres.name','genre.meta')
->get();
I find your code a bit hard to follow...
Let me try and see if I understood it correctly...
You basically have two models:
Model Collection saved in table collections
Model Genre saved in table genres
Since you have a many to many relationship between them, you need a third table to link the both of them together.
By naming convention, Laravel expects you to name it based on the two models, ordered alphabetically. So to create a link between collections and genres, you would need to create a table collection_genre which has a collection_id as a reference to the collections table, and likewise a genre_id to identify the linked genre.
You can then define your relationships as follows:
class Collection extends Model {
public function genres() {
$this->belongsToMany(\App\Models\Genre::class);
}
}
and
class Genre extends Model {
public function collections() {
$this->belongsToMany(\App\Models\Collection::class);
}
}
Now, I'm not sure what your controller looks like as the question has some invalid code to it, but I suspect you want to search the genres for a given collection.
Your code could like like this:
Class CollectionController extends Controller {
function getGenres(Collection $collection) {
return $collection->genres;
}
}
This would return the genres for the given collection.
If you want to format this, you could create an Eloquent Resource for this:
Class CollectionResource extends Resource {
public function toArray() {
return [
'name' => $this->name,
'meta' => $this->meta
];
}
}
In your controller you can then do:
Class CollectionController extends Controller {
function getGenres(Collection $collection) {
return CollectionResource::collection($collection->genres);
}
}
in your collection model
class Collections extends Model
{
protected $table='collections';
public $primaryKey='id';
protected $fillable = ['name'];
public function genres()
{
return $this->belongsToMany('App\Model\Genres','genre_collection','collection_id','genre_id')->withTimestamps();
}
}
in your genres model
class Genre extends Model {
protected $table='genres';
public $primaryKey='id';
protected $fillable = ['name'];
public function collections()
{
return $this->belongsToMany('App\Model\Collections','genre_collection','genre_id','collection_id')->get();
}
}
You are creating many to many relationship between collections and genre using genre_collection pivot table. In that case, belongsToMany is appropriate. And you don't need any model for genre_collection table.
Collections model
class Collections extends Model
{
public function genres(){
return $this->belongsToMany('App\Models\Genres', 'genre_collection', 'genre_id', 'collection_id');
}
}
Genres model
class Genres extends Model
{
public function collections(){
return $this->belongsToMany('App\Models\Collections', 'genre_collection', 'collection_id', 'genre_id');
}
}
SearchController
class SearchController extends Controller
{
foreach($collection->genres as $item){
$item->genre; // get genre info
}
}
I'm assuming that you want to access Generic directly from collection . If this is the case you can define a many-to-many relationship in collection model directly to generic model to access it . Please refer this : https://laravel.com/docs/5.7/eloquent-relationships#many-to-many . Sorry if I'm wrong
So using Laravel 4, I have a Sales table that has a many to many relationship with a Products table, and it also has a one to many relation with a Customers table.
I set up my models as follows:
class Sale extends Eloquent {
...
public function products(){
return $this->belongsToMany('Product');
}
public function customers(){
return $this->belongsTo('Customer');
}
}
class Product extends Eloquent {
...
public function sales(){
return $this->belongsToMany('Sale');
}
}
class Customer extends Eloquent {
...
public function sales(){
return $this->hasMany('Sale');
}
}
What I want to do is return the data of all sales, including the data of each product included in each sale and the data of the customer that bought it.
In my SalesController I'm using eager loading to query my data like this:
public function index()
{
return Sale::with('products', 'customers')->get();
}
It returns an object with the Sale data, the Product data, but the Customer data is null.
How can I achieve this using Eloquent (or a custom query)?
EDIT
This is the object string it returns:
[{"id":1,"customer_id":1,"date":"2013-11-21","status":1,"created_at":"0000-00-00 00:00:00","updated_at":"0000-00-00 00:00:00","products":[{"id":1,"name":"Monitor","price":50,"status":1,"created_at":"0000-00-00 00:00:00","updated_at":"0000-00-00 00:00:00","pivot":{"sale_id":1,"product_id":1,"custom_price":25,"order":1}}],"customers":null}]
Try changing your customers relationship to singular:
class Sale extends Eloquent {
...
public function products(){
return $this->belongsToMany('Product');
}
public function customer(){ // <- here
return $this->belongsTo('Customer');
}
}
(Moved from comments to answer)