Why I get BadMethodCallException? - laravel

In my seeder class, I am calling a method that is defined in a User model:
Like this:
$user = User::where('email', 'user1#teams.com')->get();
$user->test();
My User model:
class User extends Authenticatable
{
public function test()
{
return "!!";
}
}
But, when run the seed, I get this error:
[BadMethodCallException]
Method test does not exist.

the $user contains a collection of users since you are using get(). You can use first() instead.
So the new code should be:
$user = User::where('email', 'user1#teams.com')->first();

Related

Laravel error using with() method in Eloquent

I'm trying to call using with() method in Laravel Eloquent ORM, but getting the following error.
Argument 1 passed to
App\Http\Controllers\DashboardController::App\Http\Controllers\{closure}()
must be an instance of Illuminate\Database\Eloquent\Builder,
instance of Illuminate\Database\Eloquent\Relations\HasMany given
I'm using the latest version of Laravel 6. Any ideas what might have caused this?
Controller
class DashboardController extends Controller
{
public function __construct()
{
$this->middleware('auth:api');
}
public function formIndex(Request $request)
{
$id = auth()->user()->id;
$item = Groupe::find($id)->with(
[
'etudiants' => function (Builder $query) {
$query->select('id');
}
]
)->first();
return $item;
}
}
Model
class Groupe extends Authenticatable implements JWTSubject
{
public function etudiants()
{
return $this->hasMany('App\Etudiant');
}
}
The error comes from the type hint you put on the $query variable, as the error message said the object that gets passed in there is a relationship not a raw Query Builder. Just remove the type hint. Also ::find() executes a query, so you're be executing 2 queries, use the query below instead
Groupe::where('id', $id)->with(['etudiants' => function ($query) {
$query->select('id');
}])->first();
Additionally, you don't need to use the callback syntax to only eager load certain columns, the callback syntax is for putting contraints on which records get returned. Try this instead.
Groupe::where('id', $id)->with('etudiants:id,name,email')->first();
But what do you want return? groupe->students[]?? you can use
$item = Groupe::where('id',$id)->with('etudiants')->first();

Laravel Eloquent: how to set custom attribute when model is construted?

I am trying to add a custom attribute "role" to my User model. My current implementation is like this:
User extends Authenticatable
{
protected $appends = array('role');
public $getRoleAttribute()
{
$role = DB::table('acl_user_has_roles')->where('user_id', $this->id)
->value('role');
return $role;
}
}
This implementation largely works. The concern is, this role attribute is referenced many times in the life time of $user instance. Whenever it is reference, the getRoleAttribute() function will be called, then database queries will be executed. It seems a bit unnecessary to me, so I am trying find a way to only run these queries once, preferably when model instance is contructed:
I tried to override model constructor as described in answer to another similar question:
public $role;
public function __construct(array $attributes = array())
{
parent::__construct($attributes);
$this->role= $this->role();
}
protected function role()
{
$role = DB::table('acl_user_has_roles')->where('user_id', $this->id)
->value('role');
return $role;
}
When I tried to reference the role attribute like this:
$user = User::find(1);
echo $user->role;
I get nothing.
if I simply set the role attribute to some dummy text:
$this->role = "Dummy Role";
instead of:
$this->role();
Then I can get this "Dummy Role" text.
What am I missing here?

PhpUnit - mocking laravel model with relations

I'm trying to mock (it's example only) $user->posts()->get().
example service:
use App\Models\User;
class SomeClass{
public function getActivePost(User $user): Collection
{
return $user->posts()->get();
}
}
and my Model:
and Model:
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use \App\Models\Post;
class User extends Model
{
public function posts() : HasMany
{
return $this->hasMany(Post::class);
}
}
this doesn't work:
$this->user = Mockery::mock(User::class);
$this->user
->shouldReceive('wallets->get')
->andReturn('test output');
error:
TypeError: Return value of Mockery_2_App_Models_User::posts() must be an instance of Illuminate\Database\Eloquent\Relations\HasMany, instance of Mockery_4__demeter_posts returned
without return type hint (on post() method) everything is ok. Must I modify andReturn()? idk how
This error can be solved by using the alias prefix with a valid class name. Like the following:
$m = m::mock('alias:App\Models\User');
More information can be found at the official documentation http://docs.mockery.io/en/latest/reference/creating_test_doubles.html#aliasing
Alternatively you can use like this.
use App\Models\User;
class SomeClass{
public function getActivePost(User $user): Collection
{
$user->load('posts');
return $user->posts;
}
}
First you need to mock post, then add it to Collection (don't forget to use it in the top). Then when you call posts attribute its takes mocked $posts. In this case it will not throw error about return type.
use Illuminate\Database\Eloquent\Collection;
$post = $this->mock(Post::class)->makePartial();
$posts = new Collection([$post]);
$this->user = Mockery::mock(User::class);
$this->user
->shouldReceive('getAttribute')
->with('posts');
->andReturn($posts);
Also i wouldn't use mocks here. There is absolutely no need for it. So the unit test i write would be:
Create a user.
Create some posts authored by the user.
Perform assertions on user & posts.
So the code will then be something like this in my test:
$user = factory(User::class)->create();
$posts = factory(Post::class, 5)->create(['user_id' => $user->id]);
$this->assertNotEmpty($user->id);
$this->assertNotEmpty($posts);
$this->assertEquals(5, $posts->fresh()->count());
$this->assertEquals($user->id, $post->fresh()->first()->user_id);
if you want to test the relationship you can:
/** #test */
function user_has_many_posts()
{
$user = factory(User::class)->create();
$post= factory(Post::class)->create(['user_id' => $user->id]);
//Check if database has the post..
$this->assertDatabaseHas('posts', [
'id' => $post->id,
'user_id' => $user->id,
]);
//Check if relationship returns collection..
$this->assertInstanceOf('\Illuminate\Database\Eloquent\Collection', $user->posts);
}

Zizaco Entrust 'hasRole' not working within Laravel controller

User model:
<?php
use Illuminate\Auth\UserInterface;
use Illuminate\Auth\Reminders\RemindableInterface;
use Zizaco\Entrust\HasRole;
class User extends Eloquent implements UserInterface, RemindableInterface {
use HasRole;
Role Model:
<?php
use Zizaco\Entrust\EntrustRole;
class Role extends EntrustRole
{
}
Permission Model:
<?php
use Zizaco\Entrust\EntrustPermission;
class Permission extends EntrustPermission
{
}
User controller:
public function postSignin(){
if (Auth::attempt(array('email'=>Input::get('email'),
'password'=>Input::get('password')))) {
$id = Auth::user()->id;
$user = User::where('id','=',$id);
$firstname = Auth::user()->firstname;
if ($user->hasRole("User_Not_Approved")) {
return Redirect::intended('/users/dashboard');
}
Error message:
BadMethodCallException
Call to undefined method Illuminate\Database\Query\Builder::hasRole()
The error message is presented when the IF statement is running, whilst the user is logging in. I have followed Entrust's instructions, but I am at a loss as to why it isn't picking up the method.
Any help would be hugely appreciated!
Try changing: $user = User::where('id','=',$id); to $user = User::find($id);
User::where would need ->get to return what you want, and even then it would return a collection; you would want something like User::where(etc)->first(); to ensure you got a single instance of User. In reality though, since you are retrieving by id, that is what ->find($id) is designed for, and what you should do.

Laravel 4 arg1 must be an instance of UserInterface when using Auth::login

I have a basic laravel 4 app that allows someone to register and then login. I am trying to make it so that when a user completes their registration successfully they are logged in automatically. I get an error exception 'Argument 1 passed to Illuminate\Auth\Guard::login() must be an instance of Illuminate\Auth\UserInterface, instance of User given'. I understand that this means that the first argument being passed to the login method is not correct but I don't understand why it is not correct when the laravel documentation says to use
$user = User::find(1);
Auth::login($user);
Here is my controller
<?php
Class UsersController extends BaseController {
public $restful = 'true';
protected $layout = 'layouts.default';
public function post_create()
{
$validation = User::validate(Input::all());
if ($validation->passes()) {
User::create(array(
'username'=>Input::get('username'),
'password'=>Hash::make(Input::get('password'))
));
$user = User::where('username', '=', Input::get('username'))->first();
Auth::login($user);
return Redirect::Route('home')->with('message', 'Thanks for registering! You are now logged in!');
}
else {
return Redirect::Route('register')->withErrors($validation)->withInput();
}
}
}
?>
There's a few scenarios I can think of:
You're not using the User model which comes with a fresh Laravel install (sounds unlikely, but that one implements UserInterface, it's possible yours does not if you've edited it or created a new one).
User::create() isn't successfully being called (isn't created a user successfully)
$user = User::where()->... isn't resulting in a result
Try:
$user = User::create(array(
'username'=>Input::get('username'),
'password'=>Hash::make(Input::get('password'))
));
Auth::login($user);
If you still get errors, it's likely that $user isn't a User object because the user wasn't created successfully.
make sure your User.php begins like..
use Illuminate\Auth\UserInterface;
use Illuminate\Auth\Reminders\RemindableInterface;
class User extends Eloquent implements UserInterface, RemindableInterface {

Resources