Laravel - Array to string conversion - php

I am trying to show the related applications to abstract, I have used the code below but I am getting this error
Array to string conversion
My controller
public function show($A_ID){
$abstract = Project::find($A_ID);
// I believe the issue is caused by the line below but I am not sure what is wrong about it
$applications = Application::find($A_ID);
return view('Abstracts.show')->with('abstract', $abstract)
->with($applications);
}
EDIT: (add model v1.0 and v1.1)
My model (v1.0) which show the error of Array to string conversion
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Traits\HasCompositePrimaryKey;
class Application extends Model{
//Table name
protected $table = 'student_application';
//composite key
protected $primaryKey = array('A_ID', 'S_ID');
protected $fillable = ['S_Justification' ];
public $incrementing = false;}
My edited Model (V1.1)
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use App\Traits\HasCompositePrimaryKey;
class Application extends Model{
use HasCompositePrimaryKey;
//Table name
protected $table = 'student_application';
//composite key
protected $primaryKey = array('A_ID', 'S_ID');
protected $fillable = ['S_Justification' ];
public $incrementing = false;}
I want to note that the composite key is declared using this answer number two with currently 59 votes
For more information here is my view
#if (count($applications)>0)
#foreach ($applications as $application)
<tr>
<td><h5>{{$application->S_ID}}</h5></td>
</tr>
#endforeach
#else
<p> This project has no applications </p>
#endif

You are passing string to view.
return view('Abstracts.show')->with(['abstract'=> $abstract)];
give it a try.
Edit:
Or you can use like that.
with(array('order' => function($query)
Anyway you need to pass array in here. If you are just want to use ->with('abstract'); you need to add abstract function. For example:
public function deliveries() {
// this might be $this->hasOne... depends on what you need
return $this->hasMany('Abstracts', 'conditions', 'id')->where('foo', '!=', 'bar');
}

$applications is an object in your controller but you are accesing $applications as collection in your view file. You may try this:
$applications = Application::where('id', $A_ID)->get();
return view('Abstracts.show', compact('abstract', 'applications'));

Related

Laravel Eloquent appending a non-relation ship data with the result

I have this model file:-
namespace App;
use Illuminate\Database\Eloquent\Model;
class Observation extends Model
{
protected $fillable = ['observation', 'recommendation', 'priority', 'report_asset_id'];
protected $appends = ['facility'];
public function attachments()
{
return $this->hasMany('App\ObservationAttachment');
}
public function report_facility()
{
return $this->belongsTo('App\ReportFacility');
}
public function getFacilityAttribute()
{
return $this->report_facility()->facility;
}
}
And this is my query code:-
$observations = Observation::orderBy('created_at','desc')
->with('attachments')->get();
return response()->json($observations);
I am trying to append getFacilityAttribute to be included in result array .
I tried to use the protected $append model array but got error :-
Call to undefined method
Illuminate\Database\Eloquent\Relations\BelongsTo::facility()
The following line is incorrect:
return $this->report_facility()->facility
You are starting a query, calling the report_facility as a function (report_facility()), returns a query builder object, on which the facility function is unknown.
You should do:
return $this->report_facility->facility
In this case, eloquent will give you the ReportFacility model, from which you can retrieve the facility property or relation.
It's similar to:
return $this->report_facility()->first()->facility

Laravel object value with Blade statement does not work if model has accessor

i have an Laravel object model with accessor:
class NutritionalPlanRow extends Model
{
use HasFactory;
private $nomeAlimento;
public function __construct($aliment = null,
array $attributes = array()) {
parent::__construct($attributes);
if($aliment){
$this->aliment()->associate($aliment);
$this->nomeAlimento = $aliment->nome;
}
}
public function aliment()
{
return $this->belongsTo('App\Models\Aliment');
}
protected $guarded = [];
public function getNomeAlimentoAttribute()
{
return $this->nomeAlimento;
}
}
and i want to print the nomeAlimento value in a Blade page with Blade statement, for example:
.
.
<tbody>
#foreach( $plan->nutritionalPlanRows as $planRow )
<tr>
<td>
{{ $planRow->nomeAlimento}}
</td>
.
.
but the value inside the table cell is not printed, as if $planRow->foodName is null. In reality it is not empty, in fact if I print {{$planRow}} the structure of the object is complete, and all the attributes are set.
I noticed that if in the model I remove the accessor (getNomeAlimentoAttribute()), then the value in the blade page is correctly printed.
Why?
Thanks.
There are a few things that need attention:
First: Why do you need a constructor? You can define a calculated attribute without the constructor
use App\Models\Aliment;
class NutritionalPlanRow extends Model
{
use HasFactory;
public function aliment()
{
return $this->belongsTo(Aliment::class);
}
protected $guarded = [];
public function getNomeAlimentoAttribute()
{
return optional($this->ailment)->nome;
}
}
Second: It seems like a code smell when using constructor in Eloquent Model class to set relations. Ideally relations should be set/associated from within Controller.
Third: I feel declaring $nomeAlimento as private property on the class is not required. In Laravel calculated properties/attributes can be provided with accessors.
Update:
class Patient extends Model
{
use HasFactory;
protected $dates = ['day_born'];
protected $guarded = [];
public function getYearsAttribute(){
Log::info('patient all data '.$this); //Print correct all data
Log::info('Day'.$this->day_born); //print empty
return Carbon::parse($this->day_born)->diffForHumans(now());
}
}
Read https://carbon.nesbot.com/docs/ for more goodies.

Laravel 6 Eloquent model serialization ignoring Accessors

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

Laravel accessors using Query Builder

Trying to get Accessors in query builder but throwing error "Undefined property: stdClass::$shorcontent "
//controller
public function index(){
$articles = DB::table('articles')->paginate(10);
return view('articles.index', ['articles' => $articles], compact('articles'));
}
Here is the Model file with Accessors
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Article extends Model
{
protected $fillable = [
'user_id', 'content', 'live', 'post_on'
];
protected $guarded = ['id'];
public function getShortContentAttribute()
{
return substr($this->content,0, random_int(60, 150));
}
}
Here is the View
//article/index.blade.php View
<td class="col-md-6">{{ $article->shortcontent }} </td>
The same code working when i use eloquent instead of query builder, like this
public function index()
{
$articles = Article::paginate(10);
return view('articles.index', ['articles' => $articles], compact('articles'));
}
This answer is late and you might have found your solution, but hope it helps someone else.
Short answer, the DB facade doesn't have access to accessors and mutators defined in the model. Only objects made by instances of the model can have access to accessors and mutators.
I believe the issue here is that using the DB facade only creates the Query Builder without any reference to accessors or mutators you have set in the Article Model. DB facade only queries the database using the query builder and returns an object independent from the Article Model.
However, the Model facade will build a query builder but the instance of the object created will have access to accessors and mutators as it is an object instance of the Article Model class.
Check out this SO answer:
Difference between DB and Model facade
Accessors are only accessed once you attempt to retrieve the value of the attribute from the model instance, for example:
$article = Article::find(1);
$shortContent = $article->short_content;
This is explained further here
Thus if you wish to access accessors, then you would have to use the Model facade i.e. Article::paginate(10).
You are missing to append short_content attribute. Just add this
namespace App;
use Illuminate\Database\Eloquent\Model;
class Article extends Model
{
protected $fillable = [
'user_id', 'content', 'live', 'post_on'
];
protected $appends = ['short_content'];
protected $guarded = ['id'];
public function getShortContentAttribute()
{
return substr($this->content,0, random_int(60, 150));
}
}

How can I compact this code for sending info to the view from the Laravel Controller?

Here's my edit function in the controller
public function edit($id)
{
$game = Game::find($id);
// build list of team names and ids
$allTeams = Team::all();
$team = [];
foreach ($allTeams as $t)
$team[$t->id] = $t->name();
// build a list of competitions
$allCompetitions = Competition::all();
$competition = [];
foreach ($allCompetitions as $c)
$competition[$c->id] = $c->fullname();
return View::make('games.edit', compact('game', 'team', 'competition'));
}
I am sending data in order to display in a select list. I know about Eloquent ORM method Lists, but the problem is as far as I know it can only take property names as an argument, and not methods (like name() and fullname()).
How can I optimize this, can I still use Eloquent?
I would look into attributes and appends. You can do what you would like by adjusting your models.
Competition
<?php namespace App;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
class Competition extends Model
{
protected $appends = ['fullname'];
...
public function getFullnameAttribute()
{
return $this->name.' '.$this->venue;
}
}
Team
<?php namespace App;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
class Team extends Model
{
protected $appends = ['name'];
...
public function getNameAttribute()
{
return $this->city.' '.$this->teamName;
}
}
Controller
public function edit($id)
{
$game = Game::find($id);
$team = Team::get()->lists('id','name');
$competition = Competition::get()->lists('id','fullname');
return View::make('games.edit', compact('game', 'team', 'competition'));
}
The only thing I can think of (aside from using the map functionality of Eloquent collections) is to overwrite the toArray method in your model to add some custom attributes.
Eg.
public function toArray()
{
return array_merge(parent::toArray(), [
'fullname' => $this->fullname(),
]);
}
This will allow you to use something like:
$competition = $allCompetitions->fetch('fullname');
Although:
In saying all this I think the more elegant solution is to just provide the whole competition objects to the view and let the loop where you render them (or whatever) call the method itself.
You can call model method in view file if they are not related with other models. So if name() & fullname() returns result related to this model then you can use this model methods in view
#foreach (($allTeams as $t)
{{ $t->name() }}
#endforeach
ofcourse you have to pass the $allteams collection from controller to view

Categories