Call to a member function getPaginate() on a non-object - php

I'm new to laravel framwork , and I'm coding my first web app
and getting the following error
FatalErrorException in PersonController.php line 26:
Call to a member function getPaginate() on a non-object
this is my Controller
<?php
namespace App\Http\Controllers;
use App\Repositories\PersonRepository;
class PersonController extends Controller
{
protected $personRepo ;
protected $nbrPerPage = 4 ;
public function _construct(PersonRepository $personRepository)
{
$this->personRepo = $personRepository ;
}
public function index()
{
$persons = $this->personRepo->getPaginate(nbrPerPage);
$links = $persons->setPath('')->render();
return view('index', compact('persons', 'links'));
}
public function create()
{
}
public function store()
{
}
public function show($id)
{
//
}
public function edit($id)
{
//
}
public function update($id)
{
//
}
public function destroy($id)
{
//
}
}
and this my repository class
<?php
namespace App\Repositories ;
use App\Person ;
use App\User;
class PersonRepository {
protected $person ;
public function _construct (Person $person)
{
$this->$person = $person ;
}
public function getPaginate($n)
{
return $this->person-> paginate($n) ;
}
}

You are instantiating an empty instance of the Person model and then trying to call paginate() on it in your repository. However, paginate() is meant to be called on either a query builder object or an Eloquent query. Assuming that you want to return paginated results of all your models, you can scrap the $person property entirely as well as the constructor and then just change your method to this:
public function getPaginate($n)
{
return Person::paginate($n) ;
}
I will say that for such a simple query, I would suggest not using a repository altogether and just use Person::paginate($n) inside your controller, as Eloquent essentially functions as a repository already.

Unless these are just typos in the question, you have a lot of typos in your code.
The typo that is causing this specific error is that the name of the constructor method should be __construct (with two underscores), not _construct (with one underscore).
Since the constructor method is misspelled on your PersonController, this method is never called and the personRepo attribute is never set. Since it is never set, the line $persons = $this->personRepo->getPaginate(nbrPerPage); is trying to call getPaginate() on a non-object.
Additional typos/issues I see at a glance:
$persons = $this->personRepo->getPaginate(nbrPerPage);
nbrPerPage is being used as a constant. This is incorrect. Should be:
$persons = $this->personRepo->getPaginate($this->nbrPerPage);
Constructor on PersonRepository also misspelled. Should be __construct(), not _construct.
$this->$person = $person ;
This is inside the attempted construct of the PersonRepository. The $ needs to be removed from $this->$person. Should be:
$this->person = $person;

Related

How create return values when mocking Eloquent on Laravel

I am working on a project on Laravel, and I have developed my own version of User. I know that Laravel comes with it is own implementation, but just for the sake of the argument, let's forget that.
I am trying to learn how to test in Laravel, and I encountered the following problem:
Part of the controller
<?php
class UsersController extends BaseController {
protected $user;
public function __construct(User $user) {
$this->user = $user;
}
public function login() {
// Input, Validation, blah, blah blah
....
// Find user
$users = $this->user->where('email', '=', $email);
if ($users->count() == 0) {
// Do something
} else {
// Do something else different
}
// Lot more stuff...
}
}
Now comes testing. As you see I put the dependency of Eloquent through the constructor so I can mock it. The test are as follow:
<?php
class UsertTest extends TestCase {
public function __construct() {
$this->userMock = Mockery::mock('Eloquent', 'User');
}
public function tearDown() {
Mockery::close();
}
public function testLogin() {
// Unimportant
$data = ...
$this->userMock
->shouldReceive('where')
->once()
->andReturn('foo');
$this->app->instance('User', $this->userMock);
// Send the data
$this->post('/login', $data);
When I run the tests, I get this error
PHP Fatal error: Call to a member function count() on a non-object
So it seems that the mock is working, in the sense that the Database is not being hit, but now I am with the problem that I need to mock as well the value returned by the call.
How should I go about it?

Non-Static Method should not be called Statically

I am using a repository pattern and am trying to establish relationships between models. When I try to run the store() method (in the controller) which is trying to use the user() method (which establishes the relationship with the Party model), I get the following error message:
Non-static method Party::user() should not be called statically, assuming $this from incompatible context
I don't understand why I get this error when I try to run the user() relationship method, but all of the other methods (including $this->party->all(), $this->party->create($data)), work just fine.
Here is the relevant code:
// PartiesController.php
public function __construct(Party $party){
$this->party = $party
}
public function store(){
$data = Input::all();
$user = Sentry::getUser();
$this->party->user()->create($data);
}
// Party.php
class Party extends Eloquent{
public function user(){
return $this->belongsTo('User');
}
}
// User.php
use Cartalyst\Sentry\Users\Eloquent\User as SentryUserModel;
class User extends SentryUserModel implements UserInterface, RemindableInterface {
public function party(){
return $this->hasMany('Party');
}
}
// PartyRepository.php
namespace repositories\Party;
interface PartyRepository{
public function all();
public function findByID($id);
public function create($input);
public function user();
}
// EloquentPartyRepository.php
namespace repositories\Party;
use Party;
class EloquentPartyRepository implements PartyRepository{
public function all(){
return Party::all();
}
public function create($input){
return Party::create($input);
}
public function user(){
return Party::user();
}
}
The issue is because you are calling a non-static method in a static context. You may be used to seeing the way Laravel does a lot of this (e.g. User::find() and the like). These, in reality though, are not static calls (a class instance is actually being resolved behind the scenes and the find() method invoked on that instance).
In your case, it is just a plain static method call. PHP would allow this, except for the fact that in the method you are referencing $this and PHP doesn't know what to do with it. Static method calls, by definition, have no knowledge of any instances of a class.
My advice would be to inject an instance of your Model class into your repository's constructor, something like this:
//Class: EloquentPartyRepository
public function __construct(Party $party)
{
$this->party = $party;
}
public function user($partyId)
{
return $this->party->find($partyId)->user();
}
The Party instance you send to the constructor should not be a record from the database, just an empty instance of Party (i.e. new Party()), though I believe if you just add it to the constructor, the IoC should be able to leverage dependency injection and provide you with an instance.
An equivalent implementation is here, that adds a byId method:
//Class: EloquentPartyRepository
public function __construct(Party $party)
{
$this->party = $party;
}
public function byId($partyId)
{
return $this->party->find($partyId);
}
public function user($partyId)
{
if($party = $this->byId($partyId)) {
return $party->user();
}
return null;
}
I have solved the problem. Thank you #watcher and #deczo for your feedback. Both were very helpful and relevant to this error message.
In the end, I only needed to change one line. I had the sequence of method calls out of order in the store() function. Here is the relevant code.
// PartiesController.php
public function store(){
$data = Input::all();
$user = Sentry::getUser();
$user->party()->create($data);
}
In my case, to remove the non-static error and to properly insert the User model into the Party model, I only had to make the aforementioned change.
I referred to http://laravel.com/docs/eloquent/#inserting-related-models for the appropriate sequence.

Laravel/PHP: New Instance of Model Class within Method of This Model

New to Laravel and still fresh to OOP! I'm assuming this has more to do with OOP than strictly Laravel.
So my main problem is that I am trying to pass all rows from a database table called 'fin_income_category' via a method in my model called Income to a controller called PlannerController. To do this I have created a static method within Income called getIncomeCategories()
First of all, here is my __construct method within Income:
public function __construct($income, array $attributes = array()){
parent::__construct($attributes);
$this->table = $income;
}
And here is the getIncomeCategories method also within Income:
public static function getIncomeCategories(){
$category = new self('fin_income_category');
$categories = $category->all();
return $categories;
}
Finally, here is the edit($id) method within the PlannerController where I am to call this method and pass the categories along to my view. Note that only the first statement in this function is the one in question...the others work fine:
public function edit($id)
{
$income_categories = Income::getIncomeCategories();
$newIncome = new Income('fin_income');
$newRecord = $newIncome->where('id', '=', $id)->get();
return View::make('planner.edit', array('record'=>$newRecord, 'categories'=>$income_categories));
}
When I run the code like this I receive an error from Laravel:
ErrorException
Missing argument 1 for Income::__construct(),
called in /opt/lampstack/frameworks/laravel/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php on line 615 and defined
In other cases where I have instantiated a new Income I have not received this error.
Change the getIncomeCategories() to this
public static function getIncomeCategories(){
$category = new self('fin_income_category');
return $category->get()->toArray();
}
The reason why your code didn't work is because the eloquent all() found in
Illuminate\Database\Eloquent\Model; the code snippet is found below
public static function all($columns = array('*'))
{
$instance = new static;
return $instance->newQuery()->get($columns);
}
is instantiating a static class which is bound to the called class which is your Income Model Class in your case, and in the process requesting the arguments in the constructor

Laravel: Pass Parameter to Relationship Function?

Is it possible to pass, somehow, a parameter to a relationship function?
I have currently the following:
public function achievements()
{
return $this->belongsToMany('Achievable', 'user_achievements')->withPivot('value', 'unlocked_at')->orderBy('pivot_unlocked_at', 'desc');
}
The problem is that, in some cases, it does not fetch the unlocked_at column and it returns an error.
I have tried to do something like:
public function achievements($orderBy = true)
{
$result = $this->belongsToMany (...)
if($orderBy) return $result->orderBy(...)
return $result;
}
And call it as:
$member->achievements(false)->(...)
But this does not work. Is there a way to pass parameters into that function or any way to check if the pivot_unlocked_at is being used?
Well what I've did was just adding new attribute to my model and then add the my condition to that attirbute,simply did this.
Class Foo extends Eloquent {
protected $strSlug;
public function Relations(){
return $this->belongsTo('Relation','relation_id')->whereSlug($this->strSlug);
}
}
Class FooController extends BaseController {
private $objFoo;
public function __construct(Foo $foo){
$this->objFoo = $foo
}
public function getPage($strSlug){
$this->objFoo->strSlug = $strSlug;
$arrData = Foo::with('Relations')->get();
//some other stuff,page render,etc....
}
}
You can simply create a scope and then when necessary add it to a builder instance.
Example:
User.php
public function achievements()
{
return $this->hasMany(Achievement::class);
}
Achievement.php
public function scopeOrdered(Builder $builder)
{
return $builder->orderBy(conditions);
}
then when using:
//returns unordered collection
$user->achievements()->get();
//returns ordered collection
$user->achievements()->ordered()->get();
You can read more about scopes at Eloquent documentation.
You can do more simple, and secure:
When you call the relation function with the parentesis Laravel will return just the query, you will need to add the get() or first() to retrieve the results
public function achievements($orderBy = true)
{
if($orderBy)
$this->belongsToMany(...)->orderBy(...)->get();
else
return $this->belongsToMany(...)->get();
}
And then you can call it like:
$member->achievements(false);
Works for the latest version of Laravel.
Had to solve this another was as on Laravel 5.3 none of the other solutions worked for me. Here goes:
Instantiate a model:
$foo = new Foo();
Set the new attribute
$foo->setAttribute('orderBy',true);
Then use the setModel method when querying the data
Foo::setModel($foo)->where(...)
This will all you to access the attribute from the relations method
public function achievements()
{
if($this->orderBy)
$this->belongsToMany(...)->orderBy(...)->get();
else
return $this->belongsToMany(...)->get();
}

testing php laravel controller shouldReceive arguments

I have a laravel model which uses ardent/eloquent. I am trying to set up tests for the controller in particular, storing a new model that uses the ardent model.
The method works in the app but I'm having trouble with my tests
I'm having problems working out how to mock the calls this method makes.
My controllers set up and the method in question is this one:
use golfmanager\service\creator\TicketCreatorInterface;
//controller manages the ticket books
class BooksController extends BaseController {
/**
* Book Repository
*
* #var Book
*/
protected $book;
protected $ticket;
public function __construct(Book $book, TicketCreatorInterface $ticket)
{
$this->book = $book;
$this->ticket = $ticket;
}
public function store()
{
$input = Input::all();
$result = $this->book->save();
if ($result) {
//if book created then create tickets
$this->ticket->createTicket($input, $this->book);
return Redirect::route('books.index');
}
return Redirect::route('books.create')
->withInput()
->withArdentErrors()
->with('message', 'There were validation errors.');
}
And the methods used by the interface (TicketCreator):
public function createTicket($input, $book) {
//dd($input);
$counter = $input['start_number'];
while($counter <= $input['end_number']) {
$ticketDetails = array(
'ticketnumber'=>$counter,
'status'=>'unused',
'active'=>1
);
$this->ticket->create($ticketDetails)->save();
$this->ticket->book()->associate($book)->save();
$counter = $counter+1;
}
return $counter;
}
My test is as follows:
use Mockery as m;
use Way\Tests\Factory;
class BooksTest extends TestCase {
public function __construct()
{
$this->mock = m::mock('Ardent', 'Book');
$this->collection = m::mock('Illuminate\Database\Eloquent\Collection')->shouldDeferMissing();
}
public function setUp()
{
parent::setUp();
$this->attributes = Factory::book(['id' => 1, 'assigned_date'=> '20/11/2013']);
$this->app->instance('Book', $this->mock);
}
public function testStore()
{
Input::replace($input = ['start_number'=>1000, 'end_number'=>1010, 'assigned_date'=>'20/11/2013']);
$this->mock->shouldReceive('save')->once()->andReturn(true);
$this->ticket->shouldReceive('createTicket')->once()->with($input, $this->mock)->andReturn(true);
//with($input);
//$this->validate(true);
$this->call('POST', 'books');
$this->assertRedirectedToRoute('books.index');
}
Currently I get an error:
No matching handler found for Book::save()
Is this being thrown because the book model doesnt contain a save method? If it is how do I mock the model correctly. I don't want it to touch the database (although it could if it has to).
Is it the multiple saves in the createTicket method?
Still learning how to set up tests correctly - slowly getting there but not enough knowledge yet.
If I change the name of the method in shouldReceive to say 'store' it still comes up with the save() error message.
Update:
I have isolated part of the problem to the createTicket call. I've changed my testStore test and updated as above.
My error with this current test is: Undefined index: start_number.
If I remove the call to createTicket in the controller method I don't get an error. I tried using Input::replace to replace the input from a form but appears not getting through to my function
How can I simulate a form input in the mocked objects?
Thanks

Categories