Laravel accessors using Query Builder - php

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));
}
}

Related

Laravel eloquent with multiple inner joins

I currently am doing a raw sql query however this is causing issues with relationships and model boot methods.
Is it possible to do the following SQL query but with laravel eloquent models by relationship? Note all db tables have FK's defined, and relationships either HasOne or HasMany relationships.
$timeBreakDown = DB::select(
"SELECT
Entries.`task_id`,
Entries.`opportunity_id`,
SUM(Entries.`total_duration`) as 'duration',
Class.`class` as 'class',
Subclass.`sub_class` as 'subclass'
from entries Entries
INNER JOIN `tasks` Task
ON task_id = Task.id
INNER JOIN `task_class` Class
ON Task.`class_id` = Class.`id`
INNER JOIN `task_subclasses` Subclass
ON Task.`subclass_id` = Subclass.`id`
WHERE Entries.`opportunity_id` = '".$opportunity->id."'
GROUP BY Entries.`task_id`"
);
Models are
Entries
Tasks
Class
Subclass
How would I have to structure my models relationships to handle the above sql query?
You can write a query in this way:
Please check table names according to your database
DB:: table('table name')->join('tasks','task_id','=','tasks.id')
->join('task_class', 'tasks.subclass_id','=','task_class.id')
->join('task_subclasses','tasks.subclass_id','=', 'task_subclasses.id')
->selectRaw('entries.task_id,
task_subclasses.opportunity_id,
SUM(entries.total_duration) as duration,
task_class.class as class,
task_subclasses.sub_class as subclass')
->where(['entries.opportunity_id'=>$opportunity->id])
->groupBy('enteries.task_id')->get();
Models\Entries.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Entries extends Model
{
public function Tasks(){
return $this->hasOne(Tasks::class);
}
public function Class(){
return $this->hasMany(Classes::class);
}
public function SubClasses(){
return $this->hasOne(SubClasses::class);
}
}
Models\Tasks.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Tasks extends Model
{
public function Entries(){
return $this->belongsTo(Entries::class, "id", "task_id");
}
}
Models\Classes.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Classes extends Model
{
public function Entries(){
return $this->belongsTo(Entries::class, "class_id", "id");
}
}
Models\Subclasses.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class SubClasses extends Model
{
public function Entries(){
return $this->belongsTo(Entries::class, "id", "subclass_id");
}
}
Query:
Entries::with([
"Tasks",
"Classes",
"SubClasses"
])
->where("opportunity_id", $opportunity->id)
->groupBy("task_id")
->get();
Yes, You can do it with Eloquent I'll share an example with you
I can't read your Mess Query sorry for this but I will suggest you to do this
Entries::with(['Tasks','Class','Subclass'])->get();
from this, you will get all objects from this array
Let just say
The class have a relation with another Model but not Entries table then
the Eloquent is something like this
Entries::with(['Tasks','Class.Subclass'])->get();
hope its helpful for you
Might be something like this:
$timeBreakDown = Entries::select('entries.task_id, entries.opportunity_id', DB:raw('SUM(Entries.total_duration) as duration), task_class.class, task_subclasses.sub_class as subclass)
join('tasks', [['tasks.id', 'entries.task_id']])
join('task_class', [['task_class.id', 'entries.class_id']])
join('task_subclasses', [['task_subclasses.id', 'entries.subclass_id']])
->where('entries.opportunity_id', $opportunity->id)
->groupBy('entries.task_id')
->get();
Try this query:
$timeBreakDown = Entries::join('tasks', 'tasks.id', '=', 'entries.task_id')
->join('class', 'class.id', '=', 'entries.class_id')
->join('subclass', 'subclass.id', '=', 'entries.subclass_id')
->select(
'entries.task_id',
'entries.opportunity_id',
\DB::raw('SUM(entries.total_duration) as duration'),
'class.class',
'subclass.sub_class as subclass')
->where('entries.opportunity_id', $opportunity->id)
->groupBy('entries.task_id')
->get();
And try dd($timeBreakDown->toSql()); to match with your Raw SQL query.
From the official documentation.
You can define relationships using the base database relationship type by adding the tasks method to the Entries model.
The tasks method should call the hasOne method and return its result.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Entries extends Model
{
/**
* Get the phone associated with the user.
*/
public function task()
{
return $this->hasOne(Tasks::class);
}
}
In turn, the Tasks model will have an entry method with which we can determine the inverse of the hasOne relationship using the belongsTo method:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Tasks extends Model
{
/**
* Get the user that owns the phone.
*/
public function entry()
{
return $this->belongsTo(Entries::class);
}
}
you need to just setup the relationships like this:
I am assuming that that a Class will have a subClass and a Class will also have Tasks and those Tasks have Entries.
Also do you not have a User model?
Class Model
class Class extends Model
{
protected $with = ['entries', 'subclass', 'task'];
public function entries()
{
return $this->hasManyThrough(\App\Models\Entries::class, \App\Models\Task::class);
}
public function subClass()
{
return $this->hasOne(\App\Models\subClass::class);
}
public function tasks()
{
return $this->hasMany(\App\Models\Task::class);
}
}
Entry Model
class Entry extends Model
{
protected $with = ['task'];
public function task()
{
return $this->belongsTo(Task::class);
}
}
SubClass Model
class SubClass extends Model
{
protected $with = ['class'];
public function class()
{
return $this->belongsTo(\App\Models\Class::class);
}
}
Task Model
class Task extends Model
{
protected $with = ['entries', 'class'];
public function entries()
{
return $this->hasMany(\App\Models\Class::class);
}
public function class()
{
return $this->hasMany(\App\Models\Task::class);
}
}
With all of that set up you should be good to do something like this fro wherever your at in the stack:
$entry = Entry::findOrFail('id');
$entry->task->class->subClass->name;
or
$class = Class::findOrFail($class->id);
$subclass_name = $class->subclass->name;
$entries = $class->tasks->entries;
If you would have posted your models that would have been easier for us. But here is what I got from your raw query above.
$timeBreakDown = Entries::where('opportunity_id',$opportunity->id)->load('Tasks','Class.SubClass')->get();
You should read about Laravel Eloquent and relationships. Just for brief intro the difference between load and with used by Waleed is:
Load is used for lazy loading of relationship data while with is used for eager loading.
Eager loading is all the data gets load as soon as the Eloquent
queries the data while lazy loading loads the data when it is required.

Eloquent relationship in Laravel 6. Can't modify/overwrite relationship attribute

I built a one to one relationship between two Eloquent models and I want to replace an relationship attribute called profile_picture inside my User Eloquent model but I don't know how to do it.
This is a summary of my User model.
class User extends Authenticatable implements JWTSubject{
use Notifiable;
public function profile_picture(){
return $this->hasOne(UserProfilePicture::class);
}
}
And this is a summary of my UserProfilePicture model.
class UserProfilePicture extends Model{
public function user(){
return $this->belongsTo(User::class);
}
}
This is how I pretent to do it in my UserController file.
public function publicUserData($username){
$user = User::where("username", $username)->with("profile_picture")->first();
if($user){
if(!$user->profile_picture){
$user->profile_picture = UserProfilePicture::$defaultUserProfilePicture;
}
return response()->json($user);
}
return response()->json(false);
}
This is $defaultUserProfilePicture static array.
public static $defaultUserProfilePicture = [
"url" => "public/avatars/defaultUserPhoto.jpg",
"size" => 5229
];
I tried to use mutators but looks like it isn't work on relationships attributes.
I found this but doesn't work for me.
Thanks in advance.
You can provides the relation default model with attributes, you may pass an array or Closure to the withDefault method
public function profile_picture(){
return $this->hasOne(UserProfilePicture::class)->withDefault(UserProfilePicture::$defaultUserProfilePicture);
}
Reference: https://laravel.com/docs/7.x/eloquent-relationships#default-models
I solved it, first I put the static array UserProfilePicture::$defaultUserProfilePicture into my database then I do:
$user->setRelation("profile_picture", UserProfilePicture::find(1));
That take one more database request but is the one solution to I have for now.

Laravel - Array to string conversion

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'));

Laravel Jensenggers Eloquent Model sort main model with relation model

I'm trying to sort entire dataset of main model through column of relational model. I am using Laravel ORM 5.2.43 and Jensenggers MongoDb 3.1
Here are the models I have
UserEventActivity.php - Mongo Model
use Jenssegers\Mongodb\Eloquent\Model as Eloquent;
class UserEventActivity extends Eloquent
{
protected $collection = 'user_event_activity';
protected $connection = 'mongodb';
public function handset() {
return $this->hasOne('HandsetDetails', '_id', 'handset_id');
}
public function storeDetail() {
return $this->hasOne('StoreDetails', 'st_id', 'store_id');
}
}
HandsetDetails.php - Mongo Model
use Jenssegers\Mongodb\Eloquent\Model as Eloquent;
class HandsetDetails extends Eloquent
{
var $collection = 'user_handset_details';
var $connection = 'mongodb';
}
StoreDetails.php - MySql Model
use Jenssegers\Mongodb\Eloquent\HybridRelations;
use Illuminate\Database\Eloquent\Model as Eloquent;
class StoreDetails extends Eloquent
{
use HybridRelations;
protected $connection = 'mysql';
protected $table = 'icn_store';
}
Php script
$activity = UserEventActivity::join('handset ', 'handset._id', '=', 'handset_id')
->join('storeDetail', 'store_id', '=', 'storeDetail.st_id')
->orderBy('handset.handset_make', 'desc')
->select('storeDetail.*', 'handset.*')
->get()
->toArray();
This data from UserEventActivity is not stored based on handset_make field in handset relation.
Please help me to achieve the expected result
As far as I know MongoDB does not support joins like this.
A way around it could be to use eager loading.
So your UserEventActivity model might look like this:
use Jenssegers\Mongodb\Eloquent\Model as Eloquent;
class UserEventActivity extends Eloquent
{
protected $collection = 'user_event_activity';
protected $connection = 'mongodb';
public function handset() {
return $this->hasOne('HandsetDetails', '_id', 'handset_id');
}
public function storeDetail() {
return $this->hasOne('StoreDetails', 'st_id', 'store_id');
}
public function getHandsetMakeAttribute()
{
return $this->handset->handset_make;
}
}
Note the getHandsetMakeAttribute() accessor.
Then you may be able to make your call with this:
$activity = UserEventActivity::with('storeDetail')
->with('handset')
->get()
->sortByDesc('handset_make')
->toArray();
Not at all tested but worth a go.

Get array of Eloquent model's relations

I'm trying to get an array of all of my model's associations. I have the following model:
class Article extends Eloquent
{
protected $guarded = array();
public static $rules = array();
public function author()
{
return $this->belongsTo('Author');
}
public function category()
{
return $this->belongsTo('Category');
}
}
From this model, I'm trying to get the following array of its relations:
array(
'author',
'category'
)
I'm looking for a way to pull this array out from the model automatically.
I've found this definition of a relationsToArray method on an Eloquent model, which appears to return an array of the model's relations. It seems to use the $this->relations attribute of the Eloquent model. However, this method returns an empty array, and the relations attribute is an empty array, despite having my relations set up correctly.
What is $this->relations used for if not to store model relations? Is there any way that I can get an array of my model's relations automatically?
It's not possible because relationships are loaded only when requested either by using with (for eager loading) or using relationship public method defined in the model, for example, if a Author model is created with following relationship
public function articles() {
return $this->hasMany('Article');
}
When you call this method like:
$author = Author::find(1);
$author->articles; // <-- this will load related article models as a collection
Also, as I said with, when you use something like this:
$article = Article::with('author')->get(1);
In this case, the first article (with id 1) will be loaded with it's related model Author and you can use
$article->author->name; // to access the name field from related/loaded author model
So, it's not possible to get the relations magically without using appropriate method for loading of relationships but once you load the relationship (related models) then you may use something like this to get the relations:
$article = Article::with(['category', 'author'])->first();
$article->getRelations(); // get all the related models
$article->getRelation('author'); // to get only related author model
To convert them to an array you may use toArray() method like:
dd($article->getRelations()->toArray()); // dump and die as array
The relationsToArray() method works on a model which is loaded with it's related models. This method converts related models to array form where toArray() method converts all the data of a model (with relationship) to array, here is the source code:
public function toArray()
{
$attributes = $this->attributesToArray();
return array_merge($attributes, $this->relationsToArray());
}
It merges model attributes and it's related model's attributes after converting to array then returns it.
use this:
class Article extends Eloquent
{
protected $guarded = array();
public static $rules = array();
public $relationships = array('Author', 'Category');
public function author() {
return $this->belongsTo('Author');
}
public function category() {
return $this->belongsTo('Category');
}
}
So outside the class you can do something like this:
public function articleWithAllRelationships()
{
$article = new Article;
$relationships = $article->relationships;
$article = $article->with($relationships)->first();
}
GruBhub, thank you very much for your comments. I have corrected the typo that you mentioned.
You are right, it is dangerous to run unknown methods, hence I added a rollback after such execution.
Many thanks also to phildawson from laracasts, https://laracasts.com/discuss/channels/eloquent/get-all-model-relationships
You can use the following trait:
<?php
namespace App\Traits;
use Illuminate\Database\Eloquent\Relations\Relation;
trait EloquentRelationshipTrait
{
/**
* Get eloquent relationships
*
* #return array
*/
public static function getRelationships()
{
$instance = new static;
// Get public methods declared without parameters and non inherited
$class = get_class($instance);
$allMethods = (new \ReflectionClass($class))->getMethods(\ReflectionMethod::IS_PUBLIC);
$methods = array_filter(
$allMethods,
function ($method) use ($class) {
return $method->class === $class
&& !$method->getParameters() // relationships have no parameters
&& $method->getName() !== 'getRelationships'; // prevent infinite recursion
}
);
\DB::beginTransaction();
$relations = [];
foreach ($methods as $method) {
try {
$methodName = $method->getName();
$methodReturn = $instance->$methodName();
if (!$methodReturn instanceof Relation) {
continue;
}
} catch (\Throwable $th) {
continue;
}
$type = (new \ReflectionClass($methodReturn))->getShortName();
$model = get_class($methodReturn->getRelated());
$relations[$methodName] = [$type, $model];
}
\DB::rollBack();
return $relations;
}
}
Then you can implement it in any model.
<?php
namespace App;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Passport\HasApiTokens;
use App\Traits\EloquentRelationshipTrait;
class User extends Authenticatable
{
use Notifiable, HasApiTokens, EloquentRelationshipTrait;
Finally with (new User)->getRelationships() or User::getRelationships() you will get:
[
"notifications" => [
"MorphMany",
"Illuminate\Notifications\DatabaseNotification",
],
"readNotifications" => [
"MorphMany",
"Illuminate\Notifications\DatabaseNotification",
],
"unreadNotifications" => [
"MorphMany",
"Illuminate\Notifications\DatabaseNotification",
],
"clients" => [
"HasMany",
"Laravel\Passport\Client",
],
"tokens" => [
"HasMany",
"Laravel\Passport\Token",
],
]
I have published a package in order to get all eloquent relationships from a model. Such package contains the helper "rel" to do so.
Just run (Composer 2.x is required!):
require pablo-merener/eloquent-relationships
If you are on laravel 9, you are able to run artisan command model:show

Categories