Summary
I am trying to use hasManyThrough relationship with tables from different connections.
Description of the problem:
When defining a hasManyThrough relationship, the protected $connection attribute is ignored.
Steps To Reproduce:
Create the following models (Abbreviated code)
class Resource extends Model
{
protected $connection = 'tcollect'
public function absences()
{
return $this->hasManyThrough('ARM\TargetHoraire\Absence', 'ARM\Tcollect\ICO\ICOExternalReference', 'RecordID', 'ResourceID', 'ID', 'ExternalKey');
}
}
class Absence extends Model
{
protected $table = 'Absences';
protected $connection = 'punch';
}
class ICOExternalReference extends Model
{
protected $table = 'ICOExternalReferences';
protected $connection = 'tcollect';
}
Then call the relationship on the resource model
$resource->absences;
Notice that ICOExternalReference $connection attribute is ignored. It tries to use the ICOExternalReference from the punch connection.
Question
Is there any problem with my code or is there aworkaround to solve this problem?
I have created an issue on the framework repo, but it got closed right away.
Try with
hasMany(class with all parameter)
Related
Good day. I am trying to convert some of my models to use Eloquent mechanisms from table joins. I have three tables, and I want to use one of them as a pivot table. The three tables are shown below
First table (cp_cases_counsel)
Followed by (cp_counsel)
Then,(cases_sc)
I am trying to implement a many-to-many relationship between cases_sc and cp_counsel using cp_cases_counsel as the pivot table.
These are my models
use App\Models\SupremeCases;
use Illuminate\Database\Eloquent\Model;
use App\Models\CounselCase;
class Counsel extends Model
{
//
protected $fillable = [];
protected $table = 'cp_counsel';
protected $connection = 'mysql2';
public function supreme()
{
return $this->belongsToMany(SupremeCases::class,'cp_cases_counsel','counsel_id','suitno');
}
}
For the Cases Model
use Illuminate\Database\Eloquent\Model;
use App\Models\Counsel;
class SupremeCases extends Model
{
//
protected $connection = "mysql2";
protected $fillable = [];
protected $table = 'cases_sc';
public $timestamps = false;
public function counsels()
{
return $this->belongsToMany(Counsel::class,'cp_cases_counsel','counsel_id','suitno');
}
}
Getting the counsels and their cases using
$counsels = Counsel::with('supreme')->get();
I get an empty result for the supreme node in the response as shown below
Please, what I'm I doing wrong?
I'm working on a use case where one Eloquent Model should be found in multiple databases (eg. from different installations of an application).
The only way I can think of is using a super class and inherit from it with different protected $connection = 'database_connection''s. Like this:
class SuperModel extends Model
{
...
}
class SubModel1 extends SuperModel
{
protected $connection = 'connection_1';
...
}
class SubModel2 extends SuperModel
{
protected $connection = 'connection_2';
...
}
Is this the correct way to go or am I missing some Eloquent functionality that allows for this?
I'm using Laravel 6 with a SQL Server 2017 database backend. In the database I have a table called PersonPhoto, with a Photo column and a Thumbnail column where the photos and thumbnails are stored as VARBINARY.
I have defined the following Eloquent model, with two Accessors to convert the images to base64 encoding:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class PersonPhoto extends Model
{
protected $connection = 'mydb';
protected $table = 'PersonPhoto';
protected $primaryKey ='PersonID';
public function person(){
return $this->belongsTo('App\Person', 'PersonID');
}
public function getPhotoAttribute($value){
return base64_encode($value);
}
public function getThumbnailAttribute($value){
return base64_encode($value);
}
}
This works fine in Blade templates, however when I try to serialize to JSON or an Array I get a "Malformed UTF-8 characters, possibly incorrectly encoded" error, as if the Accessors are being ignored and the raw data is being serialized. To workaround this, I have altered the model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class PersonPhoto extends Model
{
protected $connection = 'mydb';
protected $table = 'PersonPhoto';
protected $primaryKey ='PersonID';
//Added to hide from and add fields to serializer
protected $hidden = ['Photo', 'Thumbnail'];
protected $appends = ['encoded_photo', 'encoded_thumbnail'];
public function person(){
return $this->belongsTo('App\Person', 'PersonID');
}
public function getPhotoAttribute($value){
return base64_encode($value);
}
public function getThumbnailAttribute($value){
return base64_encode($value);
}
//Added these new accessors
public function getEncodedPhotoAttribute(){
return base64_encode($this->Photo);
}
public function getEncodedThumbnailAttribute(){
return base64_encode($this->Thumbnail);
}
}
This hides the original Photo and Thumbnail fields from the serializer and includes the two new accessors. This appears to work and solves my issue.
Questions:
1) Is Laravel's serializer ignoring my Accessors as I suspect, and is this by design?
2) Although my workaround works, is this a reasonable approach or am I likely to run into problems? Is there a better way of doing it?
Thanks
I think you have two issues:
First, Laravel serialization requires that you append any accessors you want included — even if an attribute of the same name already exists. You did not explicitly append the desired values in the first example.
https://laravel.com/docs/5.8/eloquent-serialization#appending-values-to-json
Second, Laravel doesn't always like capitalized attribute names. It happily expects everything to be lowercase (snake_case) and based on some quick testing, seems to have some trouble associating a proper $value to pass to an accessor when case is involved.
However, you can modify your accessor to call the attribute directly instead of relying on Laravel to figure out what you are asking for and achieve the desired results.
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class PersonPhoto extends Model
{
protected $connection = 'mydb';
protected $table = 'PersonPhoto';
protected $primaryKey = 'PersonID';
// add the desired appends for serialization
protected $appends = ['Photo','Thumbnail'];
public function person()
{
return $this->belongsTo('App\Person', 'PersonID');
}
public function getPhotoAttribute()
{
// access the attribute directly
return base64_encode($this->attributes['Photo']);
}
public function getThumbnailAttribute()
{
// access the attribute directly
return base64_encode($this->attributes['Thumbnail']);
}
}
EDIT: I actually see that you did something similar in your second example with $this->Thumbnail and $this->Photo. My example is of the same concept, but without relying on magic methods.
__get/__set/__call performance questions with PHP
So I am trying to create a taggable table which enables multiple models to have tags associated with them and felt that Laravel's Polymorphic relationships would be the way to go.
Unfortunately I can't seem to get them working in the following setup. As I receive the following error when running php artisan migrate:refresh --seed.
{"error":{"type":"Symfony\\Component\\Debug\\Exception\\FatalErrorException","message":"Class name must be a valid object or a string","file":"...\\vendor\\laravel\\framework\\src\\
Illuminate\\Database\\Eloquent\\Model.php","line":527}}
I believe the issue is due to the fact that the Taggable model has the same morphTo named as outlined below. Since changing this fixes the issue. Why does this cause a problem?
Taggable model
class Taggable extends Eloquent {
protected $table = 'taggable';
public function taggable()
{
return $this->morphTo();
}
}
Track model
class Track extends Eloquent {
protected $table = 'tracks';
protected $fillable = array('title', 'year', 'image');
protected $guarded = array('id');
public function playlists()
{
return $this->belongsToMany('Playlist');
}
public function tags()
{
return $this->morphMany('Taggable', 'taggable');
}
}
Tag model
class Tag extends Eloquent {
protected $table = 'tags';
protected $fillable = array('title', 'description');
protected $guarded = array('id');
}
Migration
Schema::create('taggable', function(Blueprint $table)
{
$table->increments('id');
$table->integer('taggable_id');
$table->string('taggable_type');
$table->integer('tag_id');
$table->timestamps();
});
DatabaseSeeder Snippit
...
DB::table('taggable')->delete();
$track1 = Track::find(1);
$idm = Tag::find(1);
$track1->tags()->create(array('tag_id' => $idm->id));
...
Any help would be much appreciated on this matter.
Try simplifying your relationships - you appear to be using an intermediate table to allow for tag reuse - which is commendable but is complicating your issue.
Start with the Tag and Track models - add additional complexity after you have the basic polymorphic relationship working.
Also, the issue might be that you are using the same name for your Model as the actual relationship function is named. IE Taggable->taggable() vs Tag->taggable()
I am trying to use eloquent relationships to retrieve some data from my DB. I am running into an issue when trying to get specific fields rather than the full object. Here is some of my code.
Controller:
public function getHosts($table = null)
{
foreach (Hosts::all() as $host)
{
echo $host->physical_machine_name;
echo $host->app->id;
}
}
models:
class Hosts extends Eloquent
{
protected $connection = 'mysql_2';
protected $table = 'hosts';
public $timestamps = false;
public function app(){
return $this->hasMany('Apps', 'os_instance_name')->orWhere('os_instance_name', $this->os_instance_name);
}
}
class Apps extends Eloquent
{
protected $connection = 'mysql_2';
protected $table = 'app_instances';
public $timestamps = false;
public function host()
{
return $this->belongsTo('Hosts','os_instance_name')->orWhere('os_instance_name', $this->os_instance_name);
}
}
I am not able to get the id of the app to display but when I remove the '->id' specifier i am able to get a json object containing all of the fields. Why is this happening?
I should also note that the $host->physical_machine_name works fine.
this is the error I receive:
Undefined property: Illuminate\Database\Eloquent\Collection::$id
I am also unable to use eager loading because I am using custom foreign keys in my relationships.
You are defining apps as a hasMany relationship. This means apps will be a collection of entities, not a single one, hence the Illuminate\Database\Eloquent\Collection instance, as Laravel uses this class for lists of entities populated by Eloquent.
Being a collection of entities you won't have any properties of the App entity on it.
Instead of echo $host->app->id; you can
foreach($host->app as $app) {
echo $app->id;
}