Eager load related models in Laravel's Revisionable history - php

Using Laravel 5.4 and the VentureCraft/revisionable package.
I have 3 models: User, Company and Order.
The Order model implements the Revisionable trait:
namespace App\Models;
class Order extends Eloquent {
use \Venturecraft\Revisionable\RevisionableTrait;
// ...
}
The User model has a relationship with the Company model:
public function company() {
return $this->belongsTo('App\Models\Company');
}
And I would like to eager load the company of a user from an order revision. Something like this
$order = Order::with('revisionHistory.user.company')->find(1);
foreach($order->revisionHistory as $revision) {
$company = $revision->user->company;
}
I've tried overwriting various methods (e.g. revisionHistory and identifiableName) from the Revisionable trait without any luck.

You can use $company = $revision->userResponsible()->company to get company of the User that change the Order.

I think this is not possible with the current version of the package.
The reason behind is that userResponsible() is not an actual relationship, it's just a function that returns an instance of the User model.
To allow eager loading, something like this would be needed:
public function userResponsible()
{
if (class_exists($class = '\Cartalyst\Sentry\Facades\Laravel\Sentry')) {
return $this->belongsTo(Config::get('sentry::users')['model'], 'user_id');
} else if (class_exists($class = '\Cartalyst\Sentinel\Laravel\Facades\Sentinel')) {
return $this->belongsTo(Config::get('sentinel::users')['model'], 'user_id');
} else {
return $this->belongsTo(Config::get('auth.model'), 'user_id');
}
}
Then you would be able to eager load like this:
$order = Order::with('revisionHistory.userResponsible.company')->find(1);
You can view the original issue in GitHub.

Related

Laravel hasOne relation with where clause

I have a model called RealEstate, this model has a relation with another model called TokenPrice, I needed to access the oldest records of token_prices table using by a simple hasOne relation, So I did it and now my relation method is like following:
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasOne;
class RealEstate extends Model
{
public function firstTokenPrice(): HasOne
{
return $this->hasOne(TokenPrice::class)->oldestOfMany();
}
}
By far it's fine and no complexity. But now, I need to involve another relation into firstTokenPrice.
Let me explain a bit more:
As my project grown, the more complexity was added it, like changing firstTokenPrice using by a third table called opening_prices, so I added a new relation to RealEstate called lastOpeningPrice:
public function lastOpeningPrice(): HasOne
{
return $this->hasOne(OpeningPrice::class)->latestOfMany();
}
So the deal with simplicity of firstTokenPrice relation is now off the table, I want to do something like following every time a RealEstate object calls for its firstTokenPrice:
Check for lastOpeningPrice, if it was exists, then firstTokenPrice must returns a different record of token_price table, otherwise the firstTokenPrice must returns oldestOfMany of TokenPrice model.
I did something like following but it's not working:
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasOne;
class RealEstate extends Model
{
public function lastOpeningPrice(): HasOne
{
return $this->hasOne(OpeningPrice::class)->latestOfMany();
}
public function firstTokenPrice(): HasOne
{
$lop = $this->lastOpeningPrice;
if ($lop) {
TokenPriceHelper::getOrCreateFirstToken($this, $lop->amount); // this is just a helper function that inserts a new token price into `token_prices` table, if there was none exists already with selected amount
return $this->hasOne(TokenPrice::class)->where('amount', $lop->amount)->oldestOfMany();
}
return $this->hasOne(TokenPrice::class)->oldestOfMany();
}
}
I have checked the $this->hasOne(TokenPrice::class)->where('amount', $lop->amount)->oldestOfMany() using by ->toSql() method and it returns something unusual.
I need to return a HasOne object inside of firstTokenPrice method.
You can use ofMany builder for that purpose:
public function firstTokenPrice(): HasOne
{
$lop = $this->lastOpeningPrice;
if ($lop) {
TokenPriceHelper::getOrCreateFirstToken($this, $lop->amount); // this is just a helper function that inserts a new token price into `token_prices` table, if there was none exists already with selected amount
return $this->hasOne(TokenPrice::class)->ofMany([
'id' => 'min',
], function ($query) use ($lop) {
$query->where('amount', $lop->amount);
});
}
return $this->hasOne(TokenPrice::class)->oldestOfMany();
}
I used ->oldest() with a custom scope called amounted in TokenPrice model:
class TokenPrice extends Model
{
public function scopeAmounted(Builder $query, OpeningPrice $openingPrice): Builder
{
return $query->where('amount', $openingPrice->amount);
}
/....
}
And then changed my firstTokenPrice
public function firstTokenPrice(): HasOne
{
$lop = $this->lastOpeningPrice;
if ($lop) {
TokenPriceHelper::getOrCreateFirstToken($this, $lop->amount);
return $this->hasOne(TokenPrice::class)->amounted($lop)->oldest();
}
return $this->hasOne(TokenPrice::class)->oldestOfMany();
}
It's working, but I don't know if it's the best answer or not

one to many relationship in laravel

In my database, i have two tables notification and alertFrequency. The notification has field id and website_url and the alert frequency has id and notification_id. Both tables has models which is one to many. The notification can have one or many alertFrequency.
class Notification extends Model {
public function alertFrequencies() {
return $this - > belongsToMany('AlertFrequency::class');
}
}
namespace App;
use Illuminate\ Database\ Eloquent\ Model;
class AlertFrequency extends Model {
protected $table = 'alertFrequencies';
public function notification() {
return $this - > belongsTo(Notification::class);
}
}
in the notification model, i wrote a function called alert, that will give me the laastest alert associated with a specific websie.
public function alert(){
$alert_frequency = AlertFrequency::find('notification_id')->first();
$o_notification = $this->alertFrequencies()->where('notification_id',$alert_frequency->id)
->select('created_at')->orderBy('created_at')->first();
if($alert_frequency ==null){
return false;
}
return created_at;
}
Is this a right way to extract the data? i would appreciate any suggestions and helps?
Notification hasMany AlertFrequency
public function alertFrequencies(){
return $this->hasMany('App\AlertFrequency');
}
and,
$alert_frequency = AlertFrequency::with('notification')->orderBy('created_at','desc')->first();
loads the latest AlertFrequency along with it's notification.
See One to Many relationship and Eager loading in documentation.
to get laastest alert associated with a specific websie with url $website_url.
Notification::where('website_url', $website_url)->orderBy('created_at')->first();
hasMany relation :
public function alertFrequencies(){
return $this->hasMany('App\AlertFrequency','notification_id');
}

eager load of relations of a specific model in laravel

I have a model named Test like this:
class Test extends Model
{
public $primaryKey = 'test_id';
public function questions ()
{
return $this->belongsToMany('App\Question', 'question_test', 'test_id', 'question_id');
}
}
And a Question model like this:
class Question extends Model
{
public function tests ()
{
return $this->belongsToMany('App\Test', 'question_test', 'question_id', 'test_id');
}
}
As you see there is a ManyToMany relation between this two model.
Now in a controller function, I want to get an specific Test(by id) and send it to a view. then eager load all it's questions related models and send it to another view. like this :
public function beginTest ($course_id, $lesson_id, $test_id)
{
$test = Test::find($test_id);
if ($test->onebyone) {
return view('main/pages/test/test-onebyone', compact('test'));
} else {
$test = $test->with('questions.options')->get();
return view('main/pages/test/test-onepage', compact('test', 'done_test_id'));
}
}
}
Problem is that when I use with() laravel method to eager load relations, it return all Test models with their Question relations while I want to get relations of selected Test model only.
what is your solution to solve that?
You can use 'lazy eager loading'.
$test->load('questions.options');
Using with off the model instance will make it use a new builder and cause a new query to be executed.

laravel 4 how to retrieve logged in users data

I'm a real newbie to Laravel but I'm loving it so far. I'm struggling on thing however, I want to retrieve the data for the user that is logged in and I am not sure how to go about this.
I have a few tables but I'll keep it basic for now, I have projects table and a users table, I've defined the relationships between these two in the models as so:
user.php
public function projects() {
return hasMany('project');
}
project.php
<?php
use Illuminate\Database\Eloquent\ModelNotFoundException;
class Project extends Eloquent
{
public function user()
{
return belongsTo('user');
}
}
I know I can do the following to retrieve all projects in the database with a foreach loop, however this doesn't retrieve the logged in users projects:
$projects = DB::table('projects')->get();
I saw one tutorial which wasn't very in depth but he said to access the model query I would have to use the following command:
$project = User::all()->projects;
However this hasn't worked either. Can anyone point me into the right direction with real tutorials or post simple examples?
Thanks in advance
Those are the projects of your logged in user:
if (Auth::check())
{
$projects = Auth::user()->projects;
}
And this must be in your relation:
class User extends Eloquent
{
public function projects() {
return this->hasMany('Project');
}
}
You also need to add $this to your Project relation:
class Project extends Eloquent
{
public function user()
{
return $this->belongsTo('User');
}
}

Like or favorite a post with unique user using Laravel Eloquent

I was creating a like system for my website. in this I wanted one user can only like one time for a post. and a post can be liked by many user. Also many user can like many post.
So if I guess it right, It is a many to many reletionship.
in this context,
I create the following table
... users table:
id
name
....
posts table :
id
post
...post_likes table
id
user id
poost_id
Now I am having the following model for
user :
class User extends SentryUserModel {
public function post_likes()
{
return $this->has_many('Post_like', 'id');
}
}
post :
class Post extends Eloquent {
public function post_likes()
{
return $this->has_many('Post_like', 'id');
}
}
post_like :
class Post_like extends Eloquent {
public function posts()
{
return $this->belongs_to('Post', 'post_id');
}
public function users()
{
return $this->belongs_to('User', 'user_id');
}
}
now when I am going to insert into the database (for post_likes table) I am getting an error called
Illuminate \ Database \ Eloquent \ MassAssignmentException
user_id
Also I want to know is there any way to inset into database like
$user->like()->save($user); ?????
Thank you in advance. Happy coding . \m/
I'll start with a basic issue, firstly you might want to make sure all your tables are lower case (still as a snake case as well), it's not required but it's ultimately how it's expected to be with Laravel so it makes life easier to keep with that. Also a note to the wise, like Class names, database tables are typically in the singular so user instead of users
Secondly yes you can do an insert with $user->post_likes()->save($debate); as your post_likes method on the user class returns has_many.
Thirdly, your design of the Post_like class is a bit off, you could be better off make it like so:
class PostLike extends Eloquent { // note that PostLikes is a more standard naming for a class, they should ideally be camel case names but with all capitals for words
protected $table = 'post_like'; // specifies the table the model uses
public function post() // this should be singular, the naming of a belngs_to method is important as Laravel will do some of the work for you if let it
{
return $this->belongs_to('Post'); // by naming the method 'post' you no longer need to specify the id, Laravel will automatically know from the method name and just adding '_id' to it.
}
public function users()
{
return $this->belongs_to('User');
}
}
Fourthly, your other classes could be better as:
class Post extends Eloquent {
public function post_likes()
{
return $this->has_many('PostLike'); // doesn't require you to specify an id at all
}
}
I can't exactly tell you why you're getting that mass assign error, your post is a bit garbled and doesn't look like you've included the code that actually causes the exception? I have a feeling though is that you're trying to do an insert for multiple database rows at one time but haven't defined a fillable array for PostLike such as with here: http://four.laravel.com/docs/eloquent#mass-assignment
According to Laravel 5:
User Model:
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model {
public function post_likes()
{
return $this->hasMany('App\PostLike');
}
}
Post Model:
namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model {
public function post_likes()
{
return $this->hasMany('App\PostLike');
}
}
PostLike Model:
namespace App;
use Illuminate\Database\Eloquent\Model;
class PostLike extends Model {
protected $table = 'post_like';
public function posts()
{
return $this->belongsTo('App\Post');
}
public function users()
{
return $this->belongsTo('App\User');
}
}
and if you want to save the post_like data then:
$inputs['post_id'] = 1;
$inputs['user_id'] = 4;
$post_like = PostLike::create($inputs);
Hope this helps!!

Categories