I am using the User class included with laravel 4. I am trying to store a new question that belongs to the user and the user needs to be logged in to create. when I call the questions controller action store I get the following error
Class User contains 2 abstract methods and must therefore be declared abstract or implement the remaining methods (Illuminate\Auth\UserInterface::getAuthPassword, Illuminate\Auth\Reminders\RemindableInterface::getReminderEmail)
I have read a bit on abstract methods in php and while I don't completely understand them the error itself gives two solutions to the problem, declare the class abstract of implement the remaing methods. I am guessing that since this is the model class that ships with laravel that the correct solution is not to change its declaration to abstract but to implement the remaining methods. How do I do this correctly in this case and going forward?
User Model
<?php
use Illuminate\Auth\UserInterface;
use Illuminate\Auth\Reminders\RemindableInterface;
class User extends BaseModel implements UserInterface, RemindableInterface {
protected $guarded = [];
public static $rules = array(
'username' => 'required|unique:users|alpha_dash|min:4',
'password' => 'required|alpha_num|between:4,8|confirmed',
'password_confirmation'=>'required|alpha_num|between:4,8'
);
public function Questions($value='')
{
return $this->hasMany('Question');
}
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'users';
/**
* The attributes excluded from the model's JSON form.
*
* #var array
*/
protected $hidden = array('password');
/**
* Get the unique identifier for the user.
*
* #return mixed
*/
public function getAuthIdentifier()
{
return $this->getKey();
}
/**
* Get the password for the user.
*
* #return string
*/
public function getAuthPassword()
{
return $this->password;
}
/**
* Get the e-mail address where password reminders are sent.
*
* #return string
*/
public function getReminderEmail()
{
return $this->email;
}
}
Questions Controller
/**
* Store a newly created resource in storage.
*
* #return Response
*/
public function postStore()
{
$validation = Question::validate(Input::all());
if($validation->passes()) {
Question::create(array(
'question'=>Input::get('question'),
'user_id'=>Auth::user()->id
));
return Redirect::Route('home')
->with('message', 'Your question has been posted.');
} else {
return Redirect::to('user/register')->withErrors($validation)
->withInput();
}
}
edit 1: The error message includes '(Illuminate\Auth\UserInterface::getAuthPassword, Illuminate\Auth\Reminders\RemindableInterface::getReminderEmail)' these two methods are in my user.php as publice functions as you can see above, so do I need to do something else to 'implement' them?
edit 2:
Laravel Src UserInterface Class
<?php namespace Illuminate\Auth;
interface UserInterface {
/**
* Get the unique identifier for the user.
*
* #return mixed
*/
public function getAuthIdentifier();
/**
* Get the password for the user.
*
* #return string
*/
public function getAuthPassword();
}
laravel src RemindableInterface class
<?php namespace Illuminate\Auth\Reminders;
interface RemindableInterface {
/**
* Get the e-mail address where password reminders are sent.
*
* #return string
*/
public function getReminderEmail();
}
edit 3:
php.ini related to error reporting
; error_reporting
; Default Value: E_ALL & ~E_NOTICE
; Development Value: E_ALL | E_STRICT
; Production Value: E_ALL & ~E_DEPRECATED
error_reporting = E_ALL
; Eval the expression with current error_reporting(). Set to true if you want
; error_reporting(0) around the eval().
; http://php.net/assert.quiet-eval
;assert.quiet_eval = 0
basemodel class
<?php
class Basemodel extends Eloquent {
public static function validate($data) {
return Validator::make($data, static::$rules);
}
}
?>
edit 4;
Adding correct model class as it was when giving the error and how it is now with the fix
<?php
use Illuminate\Auth\UserInterface;
use Illuminate\Auth\Reminders\RemindableInterface;
class Question extends BaseModel implements UserInterface, RemindableInterface {
protected $guarded = [];
public static $rules = array(
'questions'=>'required|min:10|max:255',
//'solved'=>'in:0,1',
);
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'questions';
/**
* The attributes excluded from the model's JSON form.
*
* #var array
*/
protected $hidden = array('');
/**
* Get the unique identifier for the question.
*
* #return mixed
*/
public function getAuthIdentifier()
{
return $this->getKey();
}
public function user()
{
return $this->belongsTo('User');
}
}
add this to fix
/**
* Get the password for the user.
*
* #return string
*/
public function getAuthPassword()
{
return $this->password;
}
/**
* Get the e-mail address where password reminders are sent.
*
* #return string
*/
public function getReminderEmail()
{
return $this->email;
}
Perhaps it's easiest to answer this with an example. Say I have the following classes:
abstract class ClassB {
public abstract function foo();
}
class ClassA extends ClassB {}
$a = new ClassA();
Running this code will result in the following error:
Fatal error: Class ClassA contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (ClassB::foo)
This means I'm missing an implementation of foo() (defined in ClassB) in ClassA. Abstract methods can only be defined in abstract classes, and mean that any non-abstract derived class must expose a full implementation - which ClassA doesn't in this case. The above example can be fixed by changing ClassA to
class ClassA extends ClassB {
// implementation of abstract ClassB::foo().
public function foo() {
echo 'Hello!';
}
}
Back to your example. Your User class extends BaseModel. Depending on whether BaseModel extends another abstract class, it will contain two methods defined as abstract that your User class is missing. You need to find these methods - my error message explicitly told me what I was missing - and implement them in User.
Related
I'm using https://github.com/spatie/laravel-permission
I have created a new class which extends the Role class. Here is the code for Role:
<?php
namespace Spatie\Permission\Models;
use Illuminate\Database\Eloquent\Model;
use Spatie\Permission\Traits\HasPermissions;
use Spatie\Permission\Exceptions\RoleDoesNotExist;
use Spatie\Permission\Contracts\Role as RoleContract;
use Spatie\Permission\Traits\RefreshesPermissionCache;
class Role extends Model implements RoleContract
{
use HasPermissions;
use RefreshesPermissionCache;
/**
* The attributes that aren't mass assignable.
*
* #var array
*/
public $guarded = ['id'];
/**
* Create a new Eloquent model instance.
*
* #param array $attributes
*/
public function __construct(array $attributes = [])
{
parent::__construct($attributes);
$this->setTable(config('laravel-permission.table_names.roles'));
}
/**
* A role may be given various permissions.
*
* #return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function permissions()
{
return $this->belongsToMany(
config('laravel-permission.models.permission'),
config('laravel-permission.table_names.role_has_permissions')
);
}
/**
* A role may be assigned to various users.
*
* #return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function users()
{
return $this->belongsToMany(
config('auth.model') ?: config('auth.providers.users.model'),
config('laravel-permission.table_names.user_has_roles')
);
}
/**
* Find a role by its name.
*
* #param string $name
*
* #throws RoleDoesNotExist
*
* #return Role
*/
public static function findByName($name)
{
$role = static::where('name', $name)->first();
if (! $role) {
throw new RoleDoesNotExist();
}
return $role;
}
/**
* Determine if the user may perform the given permission.
*
* #param string|Permission $permission
*
* #return bool
*/
public function hasPermissionTo($permission)
{
if (is_string($permission)) {
$permission = app(Permission::class)->findByName($permission);
}
return $this->permissions->contains('id', $permission->id);
}
}
My code was working fine when accessing this Role class directly for create()'s, but attempting to perform the same tasks using my new UserRole class, I am getting Column not found database errors when attempting to create a new Role.
Here is the UserRole class:
namespace App;
use Spatie\Activitylog\Traits\LogsActivity;
use Spatie\Permission\Models\Role;
class UserRole extends Role
{
use LogsActivity;
/**
* The attributes that should be logged.
*
* #var array
*/
protected static $logAttributes = ['name', 'permissions'];
}
So Role::create() works fine, but UserRole::create() does not.
Well changing the name to Role and then changing my use clause to as SpatieRole has fixed the issue. I'm guessing it was some type of class name relationship issue with Eloquent.
If you don't define the $table property on your Eloquent model, the table name is derived from the name of the Model. So, the Role model would use the roles table by default. The UserRole model would look for the user_roles table by default.
Since you still want to use the same table, but your model name is changed, you will need to define the $table property on your new model to make it look at the roles table.
class UserRole extends Role
{
protected $table = 'roles';
// ...
}
I want to separate data from source of the data. One class for database interaction and class for data manipulation. But my approach violates LSP: preconditions cannot be strengthened in a subtype and raises strict error: Declaration of DataRepositoryItem::save() should be compatible with DataRepositoryAbstract::save(DataAbstract $data)
class DataAbstract {
}
class DataItem extends DataAbstract {
}
class DataObject extends DataAbstract {
}
abstract class DataRepositoryAbstract {
/** #return DataAbstract */
public function loadOne(){}
/** #return DataAbstract[] */
public function loadAll(){}
public function save(DataAbstract $data){}
}
class DataRepositoryItem extends DataRepositoryAbstract {
/** #return DataItem */
public function loadOne(){}
/** #return DataItem[] */
public function loadAll(){}
public function save(DataItem $data) {} // <--- violates LSP, how to avoid it?
}
class DataRepositoryObject extends DataRepositoryAbstract {
/** #return DataObject */
public function loadOne(){}
/** #return DataObject[] */
public function loadAll(){}
public function save(DataObject $data) {} // <--- violates LSP, how to avoid it?
}
How to recombine the code to fit LSP?
Update: Ok, I could rewrite methods.
class DataRepositoryItem extends DataRepositoryAbstract {
/** #return DataItem */
public function loadOne(){}
/** #return DataItem[] */
public function loadAll(){}
public function save(DataAbstract $data) {
assert($date instanceof DataItem);
//...
}
}
Works in PHP, but still violates LSP. How to avoid it?
If your language would support generics then the problem would be quite simple to solve:
public interface Repository<T> {
public void save(T data);
}
public class DataItemRepository implements Repository<DataItem> {...}
If you don't have generics then you could simply avoid trying to have a generic repository in the first place, which does more harm than good. Is there really any client code that should depend on DataRepositoryAbstract rather than a concrete repository class? If not then why forcing a useless abstraction in the design?
public interface DataItemRepository {
public DataItem loadOne();
public DataItem[] loadAll();
public void save(DataItem dataItem);
}
public class SqlDataItemRepository implements DataItemRepository {
...
}
public interface OtherRepository {
public Other loadOne();
public Other[] loadAll();
public void save(Other other);
}
Now, if somehow all save operations can be handled in a generic way you could still implement a RepositoryBase class which is extended by all repositories without violating the LSP.
public abstract class RepositoryBase {
protected genericSave(DataAbstract data) { ... }
}
public class SqlDataItemRepository extends RepositoryBase implements DataItemRepository {
public void save(DataItem item) {
genericSave(item);
}
}
However at that point you should probably use composition over inheritance by having your repositories collaborate with a GenericRepository instance:
public void save(DataItem item) {
genericRepository.save(item);
}
PS: Please note that none of the code is actual PHP code. I'm not a PHP programmer and haven't looked-up the syntax, but you should figure it out.
In any event, your inheritance hierarchy violates LSP principle, because save method and its use depends on a concrete class from an incoming object. Even if you remove a type assertion in the save method then you will not be able to use the child class DataRepositoryItem instead of parent class DataRepositoryAbstract because saving a DataItem entity differs from saving a DataAbstact entity. Let's imagine the following case of using DataRepositoryItem instead of DataRepositoryAbstract:
$repository = new DataRepositoryItem();
$entity = new DataAbstract()
// It causes incorrect behavior in DataRepositoryItem
$repository->save($entity);
We can conclude: There is no point in declaring a save method in DataRepositoryAbstract. Save method should be declared only in the concrete repository class.
abstract class DataRepositoryAbstract
{
/**
* #return DataAbstract
*/
public function loadOne(){}
/**
* #return DataAbstract[]
*/
public function loadAll(){}
}
class DataRepositoryItem extends DataRepositoryAbstract
{
/**
* #return DataItem
*/
public function loadOne(){}
/**
* #return DataItem[]
*/
public function loadAll(){}
/**
* #param DataItem
*/
public function save(DataItem $data) {}
}
class DataRepositoryObject extends DataRepositoryAbstract
{
/**
* #return DataObject
*/
public function loadOne(){}
/**
* #return DataObject[]
*/
public function loadAll(){}
/**
* #param DataObject
*/
public function save(DataObject $data) {}
}
This inheritance hierarchy provides an ability to read data from DataRepositoryObject and DataRepositoryItem the same as from DataRepositoryAbstract.
But let me ask: Where and how do you use DataRepositoryAbstract class? I sure you use it to ensure contact between concrete repository class and another code. It means that your DataRepositoryAbstract class doesn't implement any functionality, that isn't used functionally and it is a pure interface. If my assumption is valid then you should use an interface instead of an abstract class
Interfaces:
interface BaseDataRepositoryInterface
{
/**
* #return DataAbstract
*/
public function loadOne();
/**
* #return DataAbstract[]
*/
public function loadAll();
}
interface DataRepositoryItemInterface extends BaseDataRepositoryInterface
{
/**
* #return DataItem
*/
public function loadOne();
/**
* #return DataItem[]
*/
public function loadAll();
/**
* #param DataItem $data
*/
public function save(DataItem $data);
}
interface DataRepositoryObjectInterface extends BaseDataRepositoryInterface
{
/**
* #return DataObject
*/
public function loadOne();
/**
* #return DataObject[]
*/
public function loadAll();
/**
* #param DataObject $data
*/
public function save(DataObject $data);
}
Concrete implementation:
class DataRepositoryItem implements DataRepositoryItemInterface
{
public function loadOne()
{
//...
}
public function loadAll()
{
//...
}
public function save(DataItem $data)
{
//...
}
}
I am using Laravel 5.0 to create phpunit test alongside the actual model.
I get errors in phpunit tests but no errors when controller calls the model and it returned the desired data.
sample.php
<?php namespace App;
use Illuminate\Database\Eloquent\Model;
class sample extends Model {
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'sample';
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = ['id','username','details','image'];
/**
* The attributes excluded from the model's JSON form.
*
* #var array
*/
public static function test()
{
return "Returned Text.";
}
public static function gettest()
{
return self::test();
}
public static function getItem()
{
return self::orderBy('username','asc')->get();
}
public static function findItem($id)
{
return self::find($id);
}
}
SampleTest.php
<?php namespace App;
use Mockery as m;
class SampleTest extends \PHPUnit_Framework_TestCase {
protected function setUp()
{
$this->mock = m::mock('App\sample')->makePartial();
}
protected function tearDown()
{
m::close();
}
/** #test */
public function should_return_string()
{
$response = $this->mock->test();
var_dump("test() returns :".$response);
}
/** #test */
public function should_return_string_from_test_function()
{
$response = $this->mock->gettest();
var_dump("gettest() returns :".$response);
}
/** #test */
public function should_return_mocked_data()
{
$this->mock->shouldReceive('test')->andReturn('Return Mocked Data');
$response = $this->mock->gettest();
var_dump("gettest() returns :".$response);
}
/** #test */
public function should_return_some_data_using_this_mock()
{
$this->mock->shouldReceive('get')->andReturn('hello');
$response = $this->mock->getItem();
}
}
Problem
When I use controller to call the model, it returned the desired data.
When I run phpunit on command prompt:-
test function is not mocked properly as it still returns the original string
getItem and findItem functions return an error saying
1) App\SampleTest::should_return_some_data_using_this_mock
BadMethodCallException: Static method Mockery_0_App_sample::getItem()
does not exist on this mock object
Question
How can I mock the function properly? Why it is saying the error code as shown above? Where was I doing it wrong?
Any help will be much appreciated.
Note: Test assertions is removed and replaced with var_dump to see the output on the command prompt.
My error message:
Illuminate \ Container \ BindingResolutionException
Target [Project\Backend\Service\Validation\ValidableInterface] is not instantiable.
I understand that interfaces and abstract classes are not instantiable so I know that Laravel should not be trying to instantiate my interface. Yet somehow it's trying to and I suspect this may be a binding issue...even though I believe I have bound it correctly and have registered it as a service provider.
I should mention that I have taken this example out of Chris Fidao's "Implementing Laravel" and it's almost identical!
This is the first couple of lines of my form class:
namespace Project\Backend\Service\Form\Job;
use Project\Backend\Service\Validation\ValidableInterface;
use Project\Backend\Repo\Job\JobInterface;
class JobForm {
/**
* Form Data
*
* #var array
*/
protected $data;
/**
* Validator
*
* #var \Project\Backend\Form\Service\ValidableInterface
*/
protected $validator;
/**
* Job repository
*
* #var \Project\Backend\Repo\Job\JobInterface
*/
protected $job;
public function __construct(ValidableInterface $validator, JobInterface $job)
{
$this->validator = $validator;
$this->job = $job;
}
This is the first few lines of my validator class:
namespace Project\Backend\Service\Form\Job;
use Project\Backend\Service\Validation\AbstractLaravelValidator;
class JobFormValidator extends AbstractLaravelValidator {
// Includes some validation rules
This is the abstract validator:
namespace Project\Backend\Service\Validation;
use Illuminate\Validation\Factory;
abstract class AbstractLaravelValidator implements ValidableInterface {
/**
* Validator
*
* #var \Illuminate\Validation\Factory
*/
protected $validator;
/**
* Validation data key => value array
*
* #var Array
*/
protected $data = array();
/**
* Validation errors
*
* #var Array
*/
protected $errors = array();
/**
* Validation rules
*
* #var Array
*/
protected $rules = array();
/**
* Custom validation messages
*
* #var Array
*/
protected $messages = array();
public function __construct(Factory $validator)
{
$this->validator = $validator;
}
This is the code where I bind it all to the app:
namespace Project\Backend\Service\Validation;
use Illuminate\Support\ServiceProvider;
use Project\Backend\Service\Form\Job\JobFormValidator;
class ValidationServiceProvider extends ServiceProvider {
public function register()
{
$app = $this->app;
$app->bind('Project\Backend\Service\Form\Job\JobFormValidator', function($app)
{
return new JobFormValidator($app['validator']);
});
}
}
This is then registered in app/config/app.php:
.....
'Project\Backend\Service\Validation\ValidationServiceProvider',
....
Finally these are the first few lines of my controller:
use Project\Backend\Repo\Job\JobInterface;
use Project\Backend\Service\Form\Job\JobForm;
class JobController extends \BaseController {
protected $jobform;
function __construct(JobInterface $job, JobForm $jobform)
{
$this->job = $job;
$this->jobform = $jobform;
}
You need to tell Laravel which instance it should use for a certain interface when injecting it into the constructor via type hinting.
You do this using the bind() method (in your service provider for example)
$app->bind('JobInterface', 'Job'); // Job being the class you want to be used
I highly recommend you watch the video here where Taylor Otwell, the creator of Laravel, explains this and some other things.
First you need to bind using
/app/Providers/AppServiceProvider.php
<?php namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider {
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
//
}
/**
* Register any application services.
*
* #return void
*/
public function register()
{
//
$this->app->bind('JobInterface', 'Job');
}
}
Once you complete this change
Run composer update
I just have a question about add the three "RememberToken" public functions (getRememberToken(), setRememberToken(), and getRememberTokenName() ). For some reason if I tried to log in and create a new session my page would crash and I would get the "Class Foo contains 3 abstract methods..." until I had add all three to every model I had. The weird thing is that I was trying to sign in with Sessions class but I would get this error until I added the new RememberToken functions to every class I have. Is this normal? Do I need to add the "remember_token" to every table that I use now? If anyone could explain why this is or how I went wrong that would be greatly appreciated! Thanks so much!
Here is an example of one of my models with the 3 RememberToken functions:
<?php
use Illuminate\Auth\UserInterface;
use Illuminate\Auth\Reminders\RemindableInterface;
class Warranty extends Eloquent implements UserInterface, RemindableInterface {
protected $fillable = array( 'id', 'created_by', 'street_address', 'warranty_serialized');
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'warranties';
/**
* The attributes excluded from the model's JSON form.
*
* #var array
*/
protected $hidden = array('password');
/**
* Get the unique identifier for the order.
*
* #return mixed
*/
public function getAuthIdentifier()
{
return $this->getKey();
}
/**
* Get the password for the order.
*
* #return string
*/
public function getAuthPassword()
{
return $this->password;
}
/**
* Get the e-mail address where password reminders are sent.
*
* #return string
*/
public function getReminderEmail()
{
return $this->email;
}
/*4.26 Update to RememberToken*/
public function getRememberToken()
{
return $this->remember_token;
}
public function setRememberToken($value)
{
$this->remember_token = $value;
}
public function getRememberTokenName()
{
return 'remember_token';
}
}
You have to add it to the all models that
implements UserInterface, RemindableInterface
Because those are basically User tables.