I get the following error:
BadMethodCallException in Builder.php line 1999: Call to undefined method Illuminate\Database\Query\Builder::Client()
when using Laravel 5 eager loading with()
$products = Product::with(['Client'])->get();
A client can have many products. A product belongs to one client.
Product model:
<?php namespace App;
use Illuminate\Database\Eloquent\Model;
class Product extends Model {
/**
* Client who has this product.
*
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function clients()
{
return $this->belongsTo('App\Client');
}
Client model:
<?php namespace App;
use Illuminate\Database\Eloquent\Model;
class Client extends Model {
/**
* Appointment that this client has.
*
* #return \Illuminate\Database\Eloquent\Relations\hasMany
*/
public function products()
{
return $this->hasMany('App\Product');
}
Am I missing a relationship? Thanks
Your with() call should use the name of the relationship function, clients, not the name of the related class, Client. I believe it may also be case-sensitive.
$products = Product::with('clients')->get();
Related
Suppose i have a laravel model that works with a certain (MySQL) database table.
Inside of this table there is a column named 'administration'. (example: could be 1, 2 or 3)
At the moment in my controller im using that modal with eloquent and inside of my eloquent statement i ask only to work with records (crud style) that have administration 2 for instance.
I think it should be possible to add 'where administration = x' to my model instead of my controller so that if i use the model in my controller i would just be dealing with records that have that administration set.
Is this possible? I have tried googling it but could not find an answer yet.
Laravel uses global scopes for this.
You would create a scope for the administration:
<?php
namespace App\Scopes;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;
class AdministrationScope implements Scope
{
/**
* Apply the scope to a given Eloquent query builder.
*
* #param \Illuminate\Database\Eloquent\Builder $builder
* #param \Illuminate\Database\Eloquent\Model $model
* #return void
*/
public function apply(Builder $builder, Model $model)
{
$builder->where('administration', '=', 2);
}
}
And then you add it as a global scope to the model
<?php
namespace App\Models;
use App\Scopes\AdministrationScope;
use Illuminate\Database\Eloquent\Model;
class YourModel extends Model
{
/**
* The "booted" method of the model.
*
* #return void
*/
protected static function booted()
{
static::addGlobalScope(new AdministrationScope);
}
}
I found similar title and similar asked question on this website when I was researching to solve the problem. But none of posted answers helped me. This question might be duplicated but I could not solve the problem using existing questions on StackOverflow.
I'm trying to prevent access to users who are not logged in OR who are not member of "School" model!
In "web.php" file I used "middleware("auth")" to prevent access to users who are not logged in.
Now I created a "Policy" named "SchoolPolicy" to prevent access to users who are not member of "Schools" database/model.
When I call "view" method from SchoolPolicy, it prevents access for all authorized and unauthorized users!
I also checked and I realized "School" model returns "null" when I try to catch "user_id" foreign key from "schools" table.
The below piece of code is the way I created "Schools" table using Migration:
Schema::create('schools', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained();
$table->string('school_name');
$table->string('school_address')->nullable();
$table->string('school_email');
$table->string('school_phone')->nullable();
$table->string('url');
$table->longText('descriptions')->nullable();
$table->timestamps();
});
This is the route to view any school which is created by any user (URL can be dynamic)
Route::group(['middleware' => 'auth'], function () {
Route::get('/schools/{url}', [ViewSchool::class, 'index'])->name('yourschool.show');
});
And this is "School" model. I used php artisan make:model School command to create this model:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Support\Facades\Auth;
class School extends Model{
use HasFactory;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'user_id',
'school_name',
'school_address',
'school_email',
'school_phone',
'url',
'descriptions'
];
}
In this section I created School Policy. However I used Laravel 8 but I also registered created Policy manually
SchoolPolicy
<?php
namespace App\Policies;
use App\Models\School;
use App\Models\User;
use Illuminate\Auth\Access\HandlesAuthorization;
class SchoolPolicy
{
use HandlesAuthorization;
/**
* Determine whether the user can view any models.
*
* #param \App\Models\User $user
* #return mixed
*/
public function viewAny(User $user)
{
//
}
/**
* Determine whether the user can view the model.
*
* #param \App\Models\User $user
* #param \App\Models\School $school
* #return mixed
*/
public function view(User $user, School $school)
{
return $user->id == $school->user_id;
}
}
In AuthServiceProvider.php I registered SchoolPolicy like this:
<?php
namespace App\Providers;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Gate;
use App\Models\User;
use App\Models\School;
use App\Policies\SchoolPolicy;
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* #var array
*/
protected $policies = [
School::class => SchoolPolicy::class
];
/**
* Register any authentication / authorization services.
*
* #return void
*/
public function boot()
{
$this->registerPolicies();
}
}
"ViewSchool.php" file where I want to use authorize method:
<?php
namespace App\Http\Controllers\Schools;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\User;
use App\Models\School;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Auth;
class ViewSchool extends Controller
{
public function index (School $school) {
$this->authorize('view', $school);
return view('layouts.viewschool');
}
}
I tried many ways to solve the problem, but none of them properly worked:
First Try:
public function index (School $school) {
$this->authorize('view', $school);
}
Second Try:
public function index () {
$this->authorize('view', School::class);
}
I even tried to print any output from School model but I receive "null":
public function index (School $school) {
dd($school->user_id);
}
I followed all tutorials on YouTube and official Laravel website, but in my examples I gave you, authorization doesn't work properly.
Please help me to solve this problem.
Thank you
I am working on Order-Ticket functionality. Many tickets should ideally be generated for a single order.
Earlier, the responsibility of creating tickets was written inside Order Model and that time it worked fine but now that I am trying to move that responsibility to Ticket model itself, it is not working.
I'll start with the Test file 'TicketTest.php'
<?php
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use App\Concert;
use Carbon\Carbon;
use App\Order;
class TicketTest extends TestCase
{
use DatabaseMigrations;
/** #test */
function can_create_multiple_tickets_for_order(){
$numberOfTickets = 2;
// Arrange - Create Concert and Order
$concert = factory(Concert::class)->create(
[
'date' => Carbon::parse('December 1, 2016 8:00pm'),
]
);
$order = $concert->orders()->create([
'email' => 'abc#gmail.com'
]);
// Act - Create Tickets
$order->tickets()->createTickets($numberOfTickets); // This line throws call to undefined method.
// Assert
$this->assertEquals($numberOfTickets, $order->tickets()->count());
}
}
'Order.php' Model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Order extends Model
{
protected $guarded = [];
public function tickets()
{
return $this->hasMany(Ticket::class);
}
}
'Ticket.php' Model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Ticket extends Model
{
public function createTickets($ticketQuantity){
foreach(range(1, $ticketQuantity) as $i) {
$this->create([]);
}
}
public function order(){
return $this->belongsTo(Order::class);
}
}
Before Laravel 5.4, I solved this problem by creating a new Relation and using that relation to Map Order to Ticket. Created a file 'app/Relation/TicketOrderRelation.php' and added following code in it
<?php
namespace App\Relation;
use Illuminate\Database\Eloquent\Relations\HasMany;
class TicketOrderRelation extends HasMany {
/**
* Create a Collection of new tickets
*
* #param int $ticketQuantity Number of Tickets to be created
* #return \Illuminate\Database\Eloquent\Collection
*/
public function createTickets($ticketQuantity){
$instances = [];
foreach(range(1, $ticketQuantity) as $i) {
$instances[] = $this->create([]);
}
return collect($instances);
}
}
New 'Order.php' file
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use App\Relation\TicketOrderRelation;
class Order extends Model
{
protected $guarded = [];
/**
* Define a one-to-many relationship. One Order can have many tickets.
*
* This method is duplicate of hasMany method. The only difference is it
* returns object of TicketOrderRelation class at the bottom instead of
* object of HasMany class.
*
* #param string $related
* #param string $foreignKey
* #param string $localKey
* #return \App\Relation\TicketOrderRelation
*/
public function ticketOrderRelation($related, $foreignKey = null, $localKey = null)
{
$foreignKey = $foreignKey ?: $this->getForeignKey();
$instance = new $related;
$localKey = $localKey ?: $this->getKeyName();
return new TicketOrderRelation($instance->newQuery(), $this, $instance->getTable().'.'.$foreignKey, $localKey);
}
public function tickets()
{
return $this->ticketOrderRelation(Ticket::class);
}
}
New 'Ticket.php' file
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Ticket extends Model
{
protected $guarded = [];
public function order()
{
return $this->belongsTo(Order::class);
}
}
After Laravel 5.4, Eloquent builder started supporting create method which makes creation easy.
This answer shows how to create a custom Builder. I have not yet tried if custom builder solves this problem on Laravel 5.4 or not but if it does, then I would prefer that.
So, I have an 'is_abandoned' boolean on one of my tables, where if it's true, I'd like the model to be automatically excluded from any query - just like softDeletes does.
Is there something I can set in the model that'd achieve this? I'm leaning towards maybe a mutator?
These are called Global Query Scopes.
Writing a global scope is simple. Define a class that implements the Illuminate\Database\Eloquent\Scope interface. This interface requires you to implement one method: apply. The apply method may add where constraints to the query as needed:
<?php
namespace App\Scopes;
use Illuminate\Database\Eloquent\Scope;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
class AgeScope implements Scope
{
/**
* Apply the scope to a given Eloquent query builder.
*
* #param \Illuminate\Database\Eloquent\Builder $builder
* #param \Illuminate\Database\Eloquent\Model $model
* #return void
*/
public function apply(Builder $builder, Model $model)
{
$builder->where('age', '>', 200);
}
}
To assign a global scope to a model, you should override a given model's boot method and use the addGlobalScope method:
<?php
namespace App;
use App\Scopes\AgeScope;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* The "booting" method of the model.
*
* #return void
*/
protected static function boot()
{
parent::boot();
static::addGlobalScope(new AgeScope);
}
}
You can just exclude the record using eloquent where function
$lists = List::where('is_abandoned',false);
Here's an example:
Create a function within the Eloquent
class User extends Eloquent {
public function scopeAbandoned($query)
{
return $query->where('is_abandoned', '=', 1/*true*/);
}
}
Then simply use it like this:
$approvedPosts = Post::abandoned()-><put_your_own_condition>;
For detailed info read Eloquent Query Scopes.
No repeated WHERE function calls. Hope It works.
I am Trying to fetch some data for my DB by following the example shown in Laravels documentation.
But for some reason i get syntax error, unexpected '::' in HomeController.php on line 15.
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\News;
use App\Http\Requests;
use App\Http\Controllers\Controller;
class HomeController extends Controller
{
public function index()
{
$news = new::all(); // <---------------------- This line
return view ('home', ['news' => $news]);
}
}
And here is the model if needed
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class News extends Model
{
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'news';
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = ['title', 'text'];
}
new is a reserved word and can not be used as a class.
Your class is called News, not new. So change new::all(); to News::all();.