Problem with Laravel 8: Call to a member function count() on null - php

I'm a newcomer to Laravel, and I got errors when I tried to generate some info in the table in the database using a factory.
Call to a member function count() on null " at
vendor/laravel/framework/src/Illuminate/Database/Eloquent/Factories/HasFactory.php:17.
Maybe somebody had the same problem? I will be grateful if someone can help. Below will be the code of certain elements:
Seeder
class UsersTableSeeder extends Seeder
{
public function run()
{
Users::factory()->count(30)->create();
}
}
Factory
class UploadInfoFactory extends Factory
{
protected $model = Users::class;
public function definition()
{
return [
'Name' => $this->faker->name,
'Birthday' => $this->faker->date('d-m-Y'),
'Phone number' => $this->faker->phoneNumber,
'Phone balance' => $this->faker->numberBetween(-50,150),
];
}
}
DatabaseSeeder
class DatabaseSeeder extends Seeder
{
public function run()
{
$this->call(UsersTableSeeder::class);
}
}
Migration
class CreateInfoUsers extends Migration
{
public function up()
{
Schema::create('info_users', function (Blueprint $table) {
$table->integerIncrements('id');
$table->string('name',100);
$table->date('Birthday');
$table->string('Phone number',100);
$table->string('Phone balance',100);
});
}
}
The error code that pops up in bash when we enter php artisan db: seed:
Call to a member function count() on null at vendor/laravel/framework/src/Illuminate/Database/Eloquent/Factories/HasFactory.php:17
13▕ public static function factory(...$parameters): Factory {
14▕ $factory = static::newFactory() ?: Factory::factoryForModel(get_called_class());
15▕
16▕ return $factory
➜ 17▕ ->count(is_numeric($parameters[0] ?? null) ? $parameters[0] : null)
18▕ ->state(is_array($parameters[0] ?? null) ? $parameters[0] : ($parameters[1] ?? []));
19▕ }
20▕
21▕ /**

This might help someone else cause my problem was different. When seeding the database, Laravel printed out the same error that #Yurii experienced.
Seeding: Database\Seeders\ProfileSeeder
Error
Call to a member function count() on null
at vendor/laravel/framework/src/Illuminate/Database/Eloquent/Factories/HasFactory.php:18
Indeed my artisan console failed to create the Factory for my Profile model
$ php artisan tinker
>>> use Illuminate\Database\Eloquent\Factories\Factory;
>>> Factory::factoryForModel(Profile::class);
=> null
After some minutes of investigation, I found out that I forgot to return the Factory in the configure method
class ProfileFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* #var string
*/
protected $model = Profile::class;
/**
* Define the model's default state.
*
* #return array
*/
public function definition()
{
return [
// some stuff
];
}
public function configure()
{
// it miss the return here !!
$this->afterCreating(function (Profile $profile) {
// do stuff with $profile
});
}
}
So, when the Factory was instantiated, the configure method was called but no Factory was returned! Placing the return fixed the issue.

If you want a model to automatically use a factory you would have to name the factory in a particular way, otherwise you would have to define a way to resolve the particular factory you want to use for the model.
Rename your factory to UsersFactory and it will automatically be used be the Users model.
Though, I suggest renaming Users to User as the convention is for models to be in the singular and tables to be in the plural. If you change the model name you would then need to change the factory to UserFactory to match.
"The HasFactory trait's factory method will use conventions to determine the proper factory for the model. Specifically, the method will look for a factory in the Database\Factories namespace that has a class name matching the model name and is suffixed with Factory. If these conventions do not apply to your particular application or factory, you may overwrite the newFactory method on your model to return an instance of the model's corresponding factory directly"
Laravel 8.x Docs - Database Testing - Creating Models Using Factories - Connecting Factories and Models

Related

Returning null instead of "Call to undefined relationship" error for Laravel relationships

When I use ModelName::with('somerelation')->get() with Laravel Eloquent, if the model doesn't have this relationship I get Call to undefined relationship [somerelation] on model [App\SomeModel] error.
But for polymorphic relations, I get collection of all related models and I would like to use with('somerelation') and get null if relationship is not defined. Is there any way to avoid error and return null from with() or any way to use with conditionally?
What I do on all my Laravel projects is creating a Model class that extends Eloquent Model and all my models will extend my Model class, so I can override some methods from Eloquent Model using my rules.
So you can create a new class (I call it Model) and override the method with with a try/catch block retuning null in the case this exception is thrown by eloquent model.
Example:
namespace App;
use Illuminate\Database\Eloquent\Model as EloquentModel;
use Illuminate\Database\Eloquent\RelationNotFoundException;
abstract class Model extends EloquentModel
{
public static function with($relations)
{
try {
return parent::with($relations);
} catch (RelationNotFoundException $e) {
return null;
}
}
}
Previous answer by #Leonardo Oberle doesn't work for me.
I was struggling by finding a way how to validate the integrity of a relationship string passed to the with() method, so it will load relation only upon it exists, and will not throw errors if something is missing.
So, I ended up creating a abstract Model class which extends from EloquentModel and overwrites the with() method as in Leonardo Oberle's response.
The only thing is that the method's code should look like :
namespace App;
use Illuminate\Database\Eloquent\Model as EloquentModel;
use Illuminate\Database\Eloquent\RelationNotFoundException;
abstract class Model extends EloquentModel
{
public static function with($relations)
{
$filteredRelations = $this->relationExistsUntil($relations);
parent::with($filteredRelations);
}
}
/**
* #param string $with
* #return string
*/
protected function relationExistsUntil(string $with): string
{
$model = $this;
$result = '';
$relationParts = explode('.', $with);
foreach ($relationParts as $relationPart) {
$isValidRelation = method_exists($model, $relationPart) && $model->{$relationPart}() instanceof Relation;
if (!$isValidRelation) {
break;
}
$result .= empty($result) ? $relationPart : ".$relationPart";
$model = ($model->{$relationPart}()->getRelated());
}
return $result;
}
So, with that, if you will try smth like
`$user->with('relation1.relation2.relation3');`
where `relation2` doesn't exist, it will load only `relation1` and the rest will be skipped, and no exceptions/errors would be thrown.
Just don't forget to make all models then extend from that new abstract class.

Laravel 5.3 ModelFactory Seeder Call to undefined method Illuminate\Database\Query\Builder::posts()

Im trying to create a seeder table that fills a table with some dummy data using the laravel ModelFactory. I keep getting this error and I'm out of ideas on fixing it.
The 'Race' Model
<?php
namespace App\Game;
use Illuminate\Database\Eloquent\Model;
class Race extends Model
{
//
public $timestamps = false;
protected $fillable = ['name','description','icon'];
}
File 2 The Model Factory File
$factory->define(App\Game\Race::class, function (Faker\Generator $faker) {
return [
'name' => $faker->name,
'description' => $faker->text,
'icon' => $faker->url,
];
});
File 3 The Seeder File
<?php
use Illuminate\Database\Seeder;
use Illuminate\Database\Eloquent\Model;
class RacesTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* #return void
*/
public function run()
{
//
factory(App\Game\Race::class, 10)->create()->each(function($u) {
$u->posts()->save(factory(App\Game\Race::class)->make());
});
}
}
I am getting the following error
Call to undefined method Illuminate\Database\Query\Builder::posts()
This happens when I call 'php artisan db:seed'
It looks like you copied the seeder directly from the documentation. In the documentation's example, there is a User model that is related to a Posts model via a function posts that is defined within the user model. See this documentation page for more information about why there's a posts method on the User model.
You have a Race model that does not have Posts related to it, indicated by the absence of posts method inside your race model. Try simplifying the factory to this:
public function run()
{
//
factory(App\Game\Race::class, 10)->create();
}
This should create 10 race entries in your database for you. You would want to provide the closure passed to the each method only if you intend to also create other models within the seeder that are related to each instance the factory creates, or do some other action for every instance (each) of a Race that gets created by the factory.

Laravel 5: Type-hinting a FormRequest class inside a controller that extends from BaseController

I have a BaseController that provides the foundation for most HTTP methods for my API server, e.g. the store method:
BaseController.php
/**
* Store a newly created resource in storage.
*
* #return Response
*/
public function store(Request $request)
{
$result = $this->repo->create($request);
return response()->json($result, 200);
}
I then extend on this BaseController in a more specific controller, such as the UserController, like so:
UserController.php
class UserController extends BaseController {
public function __construct(UserRepository $repo)
{
$this->repo = $repo;
}
}
This works great. However, I now want to extend UserController to inject Laravel 5's new FormRequest class, which takes care of things like validation and authentication for the User resource. I would like to do this like so, by overwriting the store method and using Laravel's type hint dependency injection for its Form Request class.
UserController.php
public function store(UserFormRequest $request)
{
return parent::store($request);
}
Where the UserFormRequest extends from Request, which itself extends from FormRequest:
UserFormRequest.php
class UserFormRequest extends Request {
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'name' => 'required',
'email' => 'required'
];
}
}
The problem is that the BaseController requires a Illuminate\Http\Request object whereas I pass a UserFormRequest object. Therefore I get this error:
in UserController.php line 6
at HandleExceptions->handleError('2048', 'Declaration of Bloomon\Bloomapi3\Repositories\User\UserController::store() should be compatible with Bloomon\Bloomapi3\Http\Controllers\BaseController::store(Illuminate\Http\Request $request)', '/home/tom/projects/bloomon/bloomapi3/app/Repositories/User/UserController.php', '6', array('file' => '/home/tom/projects/bloomon/bloomapi3/app/Repositories/User/UserController.php')) in UserController.php line 6
So, how can I type hint inject the UserFormRequest while still adhering to the BaseController's Request requirement? I cannot force the BaseController to require a UserFormRequest, because it should work for any resource.
I could use an interface like RepositoryFormRequest in both the BaseController and the UserController, but then the problem is that Laravel no longer injects the UserFormController through its type hinting dependency injection.
In contrast to many 'real' object oriented languages, this kind of type hinting design in overridden methods is just not possible in PHP, see:
class X {}
class Y extends X {}
class A {
function a(X $x) {}
}
class B extends A {
function a(Y $y) {} // error! Methods with the same name must be compatible with the parent method, this includes the typehints
}
This produces the same kind of error as your code. I would just not put a store() method in your BaseController. If you feel that you are repeating code, consider introducing for example a service class or maybe a trait.
Using a service class
Below a solution that makes use of an extra service class. This might be overkill for your situation. But if you add more functionality to the StoringServices store() method (like validation), it could be useful. You can also add more methods to the StoringService like destroy(), update(), create(), but then you probably want to name the service differently.
class StoringService {
private $repo;
public function __construct(Repository $repo)
{
$this->repo = $repo;
}
/**
* Store a newly created resource in storage.
*
* #return Response
*/
public function store(Request $request)
{
$result = $this->repo->create($request);
return response()->json($result, 200);
}
}
class UserController {
// ... other code (including member variable $repo)
public function store(UserRequest $request)
{
$service = new StoringService($this->repo); // Or put this in your BaseController's constructor and make $service a member variable
return $service->store($request);
}
}
Using a trait
You can also use a trait, but you have to rename the trait's store() method then:
trait StoringTrait {
/**
* Store a newly created resource in storage.
*
* #return Response
*/
public function store(Request $request)
{
$result = $this->repo->create($request);
return response()->json($result, 200);
}
}
class UserController {
use {
StoringTrait::store as baseStore;
}
// ... other code (including member variable $repo)
public function store(UserRequest $request)
{
return $this->baseStore($request);
}
}
The advantage of this solution is that if you do not have to add extra functionality to the store() method, you can just use the trait without renaming and you do not have to write an extra store() method.
Using inheritance
In my opinion, inheritance is not so suitable for the kind of code reuse that you need here, at least not in PHP. But if you want to only use inheritance for this code reuse problem, give the store() method in your BaseController another name, make sure that all classes have their own store() method and call the method in the BaseController. Something like this:
BaseController.php
/**
* Store a newly created resource in storage.
*
* #return Response
*/
protected function createResource(Request $request)
{
$result = $this->repo->create($request);
return response()->json($result, 200);
}
UserController.php
public function store(UserFormRequest $request)
{
return $this->createResource($request);
}
You can move your logic from BaseController to trait, service, facade.
You can not override existing function and force it to use different type of argument, it would break stuff. For example, if you later would write this:
function foo(BaseController $baseController, Request $request) {
$baseController->store($request);
}
It would break with your UserController and OtherRequest because UserController expects UserController, not OtherRequest (which extends Request and is valid argument from foo() perspective).
As others have mentioned, you cannot do what you want to do for a host of reasons. As mentioned, you can solve this problem with traits or similar. I am presenting an alternative approach.
At a guess, it sounds like you are trying to follow the naming convention put forth by Laravel's RESTful Resource Controllers, which is kind of forcing you to use a particular method on a controller, in this case, store.
Looking at the source of ResourceRegistrar.php we can see that in the getResourceMethods method, Laravel does either a diff or intersect with the options array you pass in and against the default values. However, the those defaults are protected, and include store.
What this means is that you can't pass anything to Route::resource to force some override of the route names. So let's rule that out.
A simpler approach would be to simply set up a different method just for this route. This can be achieved by doing:
Route::post('user/save', 'UserController#save');
Route::resource('users', 'UserController');
Note: As per the documentation, the custom routes must come prior to the Route::resource call.
The declaration of UserController::store() should be compatible with BaseController::store(), which means (among other things) that the given parameters for both the BaseController as well as UserController should be exactly the same.
You actually cán force the BaseController to require a UserFormRequest, it's not the prettiest solution, but it works.
By overwriting there is no way you can replace Request with UserFormRequest, so why not use both? Giving both methods an optional parameter for injecting the UserFormRequest object. Which would result in:
BaseController.php
class BaseController {
public function store(Request $request, UserFormRequest $userFormRequest = null)
{
$result = $this->repo->create($request);
return response()->json($result, 200);
}
}
UserController.php
class UserController extends BaseController {
public function __construct(UserRepository $repo)
{
$this->repo = $repo;
}
public function store(UserFormRequest $request, UserFormRequest $userFormRequest = null)
{
return parent::store($request);
}
}
This way you can ignore the parameter when using BaseController::store() and inject it when using UserController::store().
The easiest and cleanest way I found to circumvent that problem was to prefix the parent methods with an underscore. For example:
BaseController:
_store(Request $request) { ... }
_update(Request $request) { ... }
UserController:
store(UserFormRequest $request) { return parent::_store($request); }
update(UserFormRequest $request) { return parent::_update($request); }
I feel like creating service providers is an overkill. What we're trying to circumvent here is not the Liskov substitution principle, but simply the lack of proper PHP reflection. Type-hinting methods is, in itself, a hack after all.
This will force you to manually implement a store and update in every child controller. I don't know if that's bothersome for your design, but in mine, I use custom requests for each controller, so I had to do it anyway.

Updating timestamps on attaching/detaching Eloquent relations

I'm using Laravel 4, and have 2 models:
class Asset extends \Eloquent {
public function products() {
return $this->belongsToMany('Product');
}
}
class Product extends \Eloquent {
public function assets() {
return $this->belongsToMany('Asset');
}
}
Product has the standard timestamps on it (created_at, updated_at) and I'd like to update the updated_at field of the Product when I attach/detach an Asset.
I tried this on the Asset model:
class Asset extends \Eloquent {
public function products() {
return $this->belongsToMany('Product')->withTimestamps();
}
}
...but that did nothing at all (apparently). Edit: apparently this is for updating timestamps on the pivot table, not for updating them on the relation's own table (ie. updates assets_products.updated_at, not products.updated_at).
I then tried this on the Asset model:
class Asset extends \Eloquent {
protected $touches = [ 'products' ];
public function products() {
return $this->belongsToMany('Product');
}
}
...which works, but then breaks my seed which calls Asset::create([ ... ]); because apparently Laravel tries to call ->touchOwners() on the relation without checking if it's null:
PHP Fatal error: Call to undefined method Illuminate\Database\Eloquent\Collection::touchOwners() in /projectdir/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php on line 1583
The code I'm using to add/remove Assets is this:
Product::find( $validId )->assets()->attach( $anotherValidId );
Product::find( $validId )->assets()->detach( $anotherValidId );
Where am I going wrong?
You can do it manually using touch method:
$product = Product::find($validId);
$product->assets()->attach($anotherValidId);
$product->touch();
But if you don't want to do it manually each time you can simplify this creating method in your Product model this way:
public function attachAsset($id)
{
$this->assets()->attach($id);
$this->touch();
}
And now you can use it this way:
Product::find($validId)->attachAsset($anotherValidId);
The same you can of course do for detach action.
And I noticed you have one relation belongsToMany and the other hasMany - it should be rather belongsToMany in both because it's many to many relationship
EDIT
If you would like to use it in many models, you could create trait or create another base class that extends Eloquent with the following method:
public function attach($id, $relationship = null)
{
$relationship = $relationship ?: $this->relationship;
$this->{$relationship}()->attach($id);
$this->touch();
}
Now, if you need this functionality you just need to extend from another base class (or use trait), and now you can add to your Product class one extra property:
private $relationship = 'assets';
Now you could use:
Product::find($validId)->attach($anotherValidId);
or
Product::find($validId)->attach($anotherValidId, 'assets');
if you need to attach data with updating updated_at field. The same of course you need to repeat for detaching.
From the code source, you need to set $touch to false when creating a new instance of the related model:
Asset::create(array(),array(),false);
or use:
$asset = new Asset;
// ...
$asset->setTouchedRelations([]);
$asset->save();
Solution:
Create a BaseModel that extends Eloquent, making a simple adjustment to the create method:
BaseModel.php:
class BaseModel extends Eloquent {
/**
* Save a new model and return the instance, passing along the
* $options array to specify the behavior of 'timestamps' and 'touch'
*
* #param array $attributes
* #param array $options
* #return static
*/
public static function create(array $attributes, array $options = array())
{
$model = new static($attributes);
$model->save($options);
return $model;
}
}
Have your Asset and Product models (and others, if desired) extend BaseModel rather than Eloquent, and set the $touches attribute:
Asset.php (and other models):
class Asset extends BaseModel {
protected $touches = [ 'products' ];
...
In your seeders, set the 2nd parameter of create to an array which specifies 'touch' as false:
Asset::create([...],['touch' => false])
Explanation:
Eloquent's save() method accepts an (optional) array of options, in which you can specify two flags: 'timestamps' and 'touch'. If touch is set to false, then Eloquent will do no touching of related models, regardless of any $touches attributes you've specified on your models. This is all built-in behavior for Eloquent's save() method.
The problem is that Eloquent's create() method doesn't accept any options to pass along to save(). By extending Eloquent (with a BaseModel) to accept the $options array as the 2nd attribute, and pass it along to save(), you can now use those two options when you call create() on all your models which extend BaseModel.
Note that the $options array is optional, so doing this won't break any other calls to create() you might have in your code.

How to replace the Laravel Builder class

I want to replace the Laravels builder class with my own that's extending from it. I thought it would be as simple as matter of App::bind but it seems that does not work. Where should I place the binding and what is the proper way to do that in Laravel?
This is what I have tried:
my Builder:
use Illuminate\Database\Eloquent\Builder as BaseBuilder;
class Builder extends BaseBuilder
{
/**
* Find a model by its primary key.
*
* #param mixed $id
* #param array $columns
* #return \Illuminate\Database\Eloquent\Model|static|null
*/
public function find($id, $columns = array('*'))
{
Event::fire('before.find', array($this));
$result = parent::find($id, $columns);
Event::fire('after.find', array($this));
return $result;
}
}
And next I tried to register the binding in bootstrap/start.php file like this :
$app->bind('Illuminate\\Database\\Eloquent\\Builder', 'MyNameSpace\\Database\\Eloquent\\Builder');
return $app;
Illuminate\Database\Eloquent\Builder class is an internal class and as such it is not dependency injected into the Illuminate\Database\Eloquent\Model class, but kind of hard coded there.
To do what you want to do, I would extend the Illuminate\Database\Eloquent\Model to MyNamespace\Database\Eloquent\Model class and override newEloquentBuilder function.
public function newEloquentBuilder($query)
{
return new MyNamespace\Database\Eloquent\Builder($query);
}
Then alias MyNamespace\Database\Eloquent\Model to Eloquent at the aliases in app/config/app.php
Both of the answers are correct in some way. You have to decide what your goal is.
Change Eloquent Builder
For example, if you want to add a new method only for eloquent models (eg. something like scopes, but maybe a little more advanced so it’s not possible in a scope)
Create a new Class extending the Eloquent Builder, for Example CustomEloquentBuilder.
use Illuminate\Database\Eloquent\Builder;
class CustomEloquentBuilder extends Builder
{
public function myMethod()
{
// some method things
}
}
Create a Custom Model and overwrite the method newEloquentBuilder
use Namespace\Of\CustomEloquentBuilder;
use Illuminate\Database\Eloquent\Model;
class CustomModel extends Model
{
public function newEloquentBuilder($query)
{
return new CustomEloquentBuilder($query);
}
}
Change Database Query Builder
For example to modify the where-clause for all database accesses
Create a new Class extending the Database Builder, for Example CustomQueryBuilder.
use Illuminate\Database\Query\Builder;
class CustomQueryBuilder extends Builder
{
public function myMethod()
{
// some method things
}
}
Create a Custom Model and overwrite the method newBaseQueryBuilder
use Namespace\Of\CustomQueryBuilder;
use Illuminate\Database\Eloquent\Model;
class CustomModel extends Model
{
protected function newBaseQueryBuilder()
{
$connection = $this->getConnection();
return new CustomQueryBuilder(
$connection, $connection->getQueryGrammar(), $connection->getPostProcessor()
);
}
}
Laravel Version: 5.5 / this code is untestet
The answer above doesn't exactly work for laravel > 5 so I done some digging and I found this!
https://github.com/laravel/framework/blob/5.2/src/Illuminate/Database/Eloquent/Model.php#L1868
use this instead!
protected function newBaseQueryBuilder()
{
$conn = $this->getConnection();
$grammar = $conn->getQueryGrammar();
return new QueryBuilder($conn, $grammar, $conn->getPostProcessor());
}

Categories