How to translate pivot values with laravel-translatable? - laravel

I just started using laravel-translatable package from dimsav which enables to set and retrieve translated strings from the database using Eloquent models. It works great but I still have trouble translating pivot values from belongsToMany relationships.
I have a model called Combination defined as shown below:
namespace App;
use Illuminate\Database\Eloquent\Model;
class Combination extends Model {
public function product() {
return $this->belongsTo('App\Product');
}
public function product_attributes() {
return $this->belongsToMany('App\Attribute')->withPivot('value');
}
}
Here is my Attribute model:
namespace App;
use Illuminate\Database\Eloquent\Model;
use Dimsav\Translatable\Translatable;
class Attribute extends Model {
use Translatable;
public $translatedAttributes = ['name'];
}
class AttributeTranslation extends Model {
public $timestamps = false;
protected $fillable = ['name'];
}
For instance, I have a Product called "T-Shirt" with two Attributes:
Color
Size
This Product has two combinations:
Black / M
Black / L
These values are stored as pivot values in an intermediate table called attribute_combination like this:
+--------------+----------------+-------+
| attribute_id | combination_id | value |
|--------------+----------------+-------|
| 1 | 1 | Black |
| 2 | 1 | M |
| 1 | 2 | Black |
| 2 | 2 | L |
+--------------+----------------+-------+
I have no problem retrieving the translated attribute's name using $attribute->name but I can't find a way to retrieve the translated pivot values. I tried to create a table called attribute_combination_translations and its AttributeCombination model as shown below but it does not work.
namespace App;
use Illuminate\Database\Eloquent\Model;
use Dimsav\Translatable\Translatable;
class AttributeCombination extends Model {
use Translatable;
protected $table = 'attribute_combination';
public $translatedAttributes = ['value'];
}
class AttributeCombinationTranslation extends Model {
public $timestamps = false;
protected $fillable = ['value'];
}
Maybe anyone using this package could help me get through this?

Related

Laravel Eloquent Many to Many Relationship between three Models

Consider I have the following table structures for tables countries, products and suppliers:
countries
------------------------
id
name
code
product
------------------------
id
name
price
suppliers
------------------------
id
name
A product can be sold in different countries but supplier of that product can be different. With that keeping in mind, I made a relations table to keep track of which supplier is delivering which product in which country:
relations
------------------------
country_id
product_id
supplier_id
Let's say I have a product A which I need to store in country US and CA but the suppliers for these countries are X and Y respectively. The structure would look something like this:
countries
-------------------------------
id | name | code
-------------------------------
1 | United States | US
2 | Canada | CA
product
-------------------------------
id | name | price
-------------------------------
1 | A | 3.99
suppliers
------------
id | name
------------
1 | X
2 | Y
relations
-------------------------------
country_id | product_id | supplier_id
-------------------------------
1 | 1 | 1
2 | 1 | 2
My question is how can I use Eloquent Relationships to this table since many-to-many relationships only work on two tables. Is there any other workaround regarding this? Or is there any other efficient way to implement this scenario?
Thank you for your help.
There is no built-in way to make a relation using three tables. Whenever I encounter something like this myself, the best solution seems to be to make an in-between model that has relations to the three tables.
So in your case, I would create a SupplierProduct that has the relations country, supplier and product.
i have also same scenario class have multiple DaysClassDetails
use this function in your parent model
public function classType()
{
return $this->hasMany('App\DaysClassDetails(middlemodel)');
}
and DaysClassDetails have multiple DaysClassTimeDetails
public function classTime()
{
return $this->hasMany('App\DaysClassTimeDetails(lastchildmodel)');
}
public function classType(){
return $this->belongsTo('App\ManageClass(parentmodel)');
}
As Jerodev suggested, I made an intermediate model SupplierProduct. Instead of making many-to-many relationships, I made one-to-many relationships with SupplierProduct and retrieved data using with functions to retrieve all data related to that record.
This is how my Models look like (database structure is same as described in question):
SupplierProduct.php
namespace App;
use Illuminate\Database\Eloquent\Model;
class SupplierProduct extends Model {
public function country() {
return $this->belongsTo(Country::class);
}
public function product() {
return $this->belongsTo(Product::class);
}
public function supplier() {
return $this->belongsTo(Supplier::class);
}
}
Country.php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Country extends Model {
public function products() {
return $this->hasMany(SupplierProduct::class)->with('product', 'supplier');
}
}
Product.php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Product extends Model {
public function products() {
return $this->hasMany(SupplierProduct::class)->with('country', 'supplier');
}
}
Supplier.php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Supplier extends Model {
public function products() {
return $this->hasMany(SupplierProduct::class)->with('country', 'product');
}
}

Laravel Eloquent: multiple foreign keys for relationship

I am in the process of porting a project to Laravel.
I have two database tables which are in a One-To-Many relationship with each other. They are joined by three conditions. How do I model this relationship in Eloquent?
I am not supposed to modify the database schema, since it has to remain backwards compatible with other things.
I have tried the following, but it doesn't work.
The owning side:
use Illuminate\Database\Eloquent\Model;
class Route extends Model
{
public function trips()
{
return $this->hasMany('Trip', 'route_name,source_file', 'route_name,source_file')
}
}
The inverse side:
use Illuminate\Database\Eloquent\Model;
class Trip extends Model
{
public function routes()
{
return $this->belongsTo('Route', 'route_name,source_file', 'route_name,source_file');
}
}
Example Route database values:
id | route_name | source_file
---------------------------------------
1 | Berlin - Paris | file1.xls
2 | Madrid - London| file2.xls
3 | Berlin - Paris | file3.xls
Example Trip database values:
id | route_name | source_file | duration
---------------------------------------------------------
1 | Berlin - Paris | file1.xls | 78
2 | Madrid - London | file2.xls | 241
3 | Berlin - Paris | file3.xls | 65
1 | Berlin - Paris | file1.xls | 95
1 | Berlin - Paris | file1.xls | 65
Route and Trip have other attributes, which I did not include here for brevity.
Is this possible in Eloquent?
I had to deal with a similar problem. The solution provided by #fab won't work with eager loading because $this->source_file would be null at the time the relationship is processed. I came up with this solution
After installing Compoships and configuring it in your models, you can define your relationships matching multiple columns.
The owning side:
use Illuminate\Database\Eloquent\Model;
use Awobaz\Compoships\Compoships;
class Route extends Model
{
use Compoships;
public function trips()
{
return $this->hasMany('Trip', ['id', 'route_name', 'source_file'], ['route_id', 'route_name', 'source_file']);
}
}
The inverse side:
use Illuminate\Database\Eloquent\Model;
use Awobaz\Compoships\Compoships;
class Trip extends Model
{
use Compoships;
public function route()
{
return $this->belongsTo('Route', ['route_id', 'route_name', 'source_file'], ['id', 'route_name', 'source_file']);
}
}
Compoships supports eager loading.
As Jonathon already mentioned, you could try to add an where-clause to your relationship:
use Illuminate\Database\Eloquent\Model;
class Route extends Model
{
public function trips()
{
return $this->hasMany('Trip', 'route_name')->where('source_file', $this->source_file);
}
}
The inverse side:
use Illuminate\Database\Eloquent\Model;
class Trip extends Model
{
public function routes()
{
return $this->belongsTo('Route', 'route_name')->where('source_file', $this->source_file);
}
}
How i would tackle this would be to do something like this
class A
{
public function b1()
{
return $this->hasMany('B', 'prop1', 'prop1')
}
public function b2()
{
return $this->hasMany('B', 'prop2', 'prop2')
}
public function b3()
{
return $this->hasMany('B', 'prop3', 'prop3')
}
public function getBsAttribute()
{
$data = collect([$this->b1, $this->b2, $this->b3]);
return $data->unique();
}
}
Collecting all the single relations and returning them as unique collection, and obviously doing the same for the inverse, this should give you some data to work with.
OP was modified so no longer relevant, left in if any one needs an answer similar

Laravel 5.2 relationships: Trying to get property of non-object

I have two models: Term and Post.
The Term model represents both categories and tags.
A post can belong only to a category and it can have many tags (I use the same db table for categories and tags).
class Term extends Model
{
protected $table = 'mydb_terms';
public $timestamps = true;
public function getRouteKeyName() {
return 'slug';
}
public function posts(){
return $this -> hasMany('Post');
}
}
class Post extends Model
{
use SoftDeletes;
protected $table = 'mydb_posts';
public $timestamps = true;
public function category(){
return $this -> belongsTo('Term','mydb_posts_terms', 'post_id', 'term_id')
-> withPivot('order') -> withTimestamps() -> where('type','category');
}
public function tags(){
return $this -> hasMany('Term') -> where('type','tag');
}
}
In the CategoryController I have:
class CategoryController extends Controller
{
public function getPostsOfCategory($id)
{
$posts = Term::find($id)->posts->get();
}
public function getPostsOfAllCategories()
{
//
}
}
When I try to retrieve the posts of category event with slug event (in the db the category and one post in it exist, and also the corresponding row in mydb_posts_terms) I obtain
Trying to get property of non-object
EDIT
I tried both $posts = Term::find($id)->posts()->get(); by obtaining Call to a member function and $posts = Term::find($id)->posts; by obtaining the same Trying to get property of non-object.
In the db I have:
Table mydb_terms:
| id | name | slug | type | created_at | updated_at
| 1 | event | event | category | 2016-07-01 18:00:00| 2016-07-01 18:00:00
Table mydb_posts:
| id | title | content | created_at | updated_at
| 1 | A title | a content | 2016-07-04 17:20:12| 2016-07-04 17:20:12
Table mydb_posts_terms:
| post_id | term_id | order | created_at | updated_at
| 1 | 1 | 0 | 2016-07-06 14:13:43| 2016-07-06 14:13:43

Laravel 5 returns string primary key as integer

I have a settings table in my database that looks like this:
| name | value | validation |
| site-name | Sample Site | max:255 |
| site-title | Sample Site | max:255 |
This is how my Setting model looks like:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Setting extends Model
{
protected $primaryKey = 'name';
protected $fillable = ['name', 'value'];
}
and the Controller.php file used for sharing the settings variable across all views:
<?php
namespace App\Http\Controllers;
use App\Setting;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Auth\Access\AuthorizesResources;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Support\Facades\Auth;
class Controller extends BaseController
{
use AuthorizesRequests, AuthorizesResources, DispatchesJobs, ValidatesRequests;
protected $usr;
protected $settings;
public function __construct()
{
$this->usr = Auth::user();
view()->share('usr', $this->usr);
$this->settings = array();
$allSettings = Setting::all();
foreach ($allSettings as $setting)
$this->settings[$setting->name] = $setting->value;
print_r($this->settings);
view()->share('settings', $this->settings);
}
}
the print_r is used for debugging.
For some reason, the print_r outputs:
Array ( [0] => Sample Site )
instead of:
Array ( [site_name] => Sample Site )
It looks like $setting->name returns an integer instead of a string (the name column on the database is set as varchar).
Any ideas why it happens?
if its not integer use in model
public $incrementing = false;
Ok, I got this.
All I needed to do is to add this line to my Setting model:
public $incrementing = false;

Polymorphic Eloquent relationships with namespaces

I've tried to implement polymorphic relationships. They work perfectly... However, I'm trying to reduce my database size as much as possible so... I've this
Table action
| id | realatable_type | relatable_id
| 1 | Lion\People | 65
| 2 | Lion\Company | 13
Obviously I've this
<?php namespace Lion;
class Company extends \Eloquent { ... }
class People extends \Eloquent { ... }
Is there any way to store only "People" or "Company" assuming that the namespace is always going to be "Lion"?
Since Laravel 4.1, inside your model (in this case Company and People) you can set the protected property $morphClass to whatever you want.
<?php namespace Lion;
class Company extends \Eloquent {
protected $morphClass = 'Company';
}
Now in your table you can store the type without the namespace:
| id | realatable_type | relatable_id
| 2 | Company | 13
I believe the best solution here (for database size at least) would be to simply change readable_type to ENUM('Lion\Company', 'Lion\People').
That being said, if you really want to handle this on Laravel side, you'll have to create new classes extending from Illuminate\Database\Eloquent\Relations\Morph* ¹ and overwrite their constructors ² as to get only the last value after a dash, on $morphClass. Something like this:
<?php
use \Illuminate\Database\Eloquent\Model;
use \Illuminate\Database\Eloquent\Builder;
class MyMorphOne extends \Illuminate\Database\Eloquent\Relations\MorphOne {
public function __construct(Builder $query, Model $parent, $type, $id) {
parent::__construct($query, $parent, $type, $id);
$this->morphClass = substr($this->morphClass, strrpos($this->morphClass, '\\') + 1);
}
}
Then, extend your own model or base model, to overwrite morphOne, morphMany and morphToMany methods as to make use of your new extended classes. Something like this:
<?php
class People extends Eloquent {
// ...
public function morphOne($related, $name, $type = null, $id = null) {
$instance = new $related;
list($type, $id) = $this->getMorphs($name, $type, $id);
$table = $instance->getTable();
return new MyMorphOne($instance->newQuery(), $this, $table.'.'.$type, $table.'.'.$id);
}
}
* = One, Many and ToMany
Which are actually inherited from MorphOneOrMany on MorphOne and MorphMany.

Resources