This question already has answers here:
Laravel seed issue, laravel is looking for plural table name
(2 answers)
Closed 1 year ago.
I have been trying to google this but I still couldn't pinpoint the exact answer to the question in my head.
I have used php artisan to create a migration file to set up a table called blogs (note the plural). Previously, the table was called blog (singular) and the factory would not seed because the error shown on the terminal was that no relations found for "blogs" - which didn't make sense to me because every reference I used in the controllers, models and factories were singular. It would not let me seed until I have rolled back the migration and re-created the table as blogs in plural form.
And the strangest thing is that I have kept everything else in singular still, anyone got any clues as to why the seeding works only when I used plural for the table's name?
This is my migration file after the table is revised from blog to blogs:
class CreateBlogsTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('blogs', function (Blueprint $table) {
$table->id();
$table->timestamps();
$table->string('title', 255);
$table->text('body');
$table->text('user_id');
});
}
This is my factory - named BlogFactory:
namespace Database\Factories;
use App\Models\Blog;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
class BlogFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* #var string
*/
protected $model = Blog::class;
/**
* Define the model's default state.
*
* #return array
*/
public function definition()
{
return [
'title' => $this->faker->sentence, //Generates a fake sentence
'body' => $this->faker->paragraph(30), //generates fake 30 paragraphs
'user_id' => User::factory() //Generates a User from factory and extracts id
];
}
}
This is my model - named Blog:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Blog extends Model
{
use HasFactory;
protected $fillable = [
'title',
'body',
'user_id',
];
}
This is my controller - named BlogController:
<?php
namespace App\Http\Controllers;
use App\Models\Blog;
use Illuminate\Http\Request;
class BlogController extends Controller
{
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
return Blog::all();
}
I seem to recall similar issues regarding renamed class-names and/or tables... Could you try running "composer dump-autoload" after having all the migrations and/or class-name refactorings ready? I remember old class-names being somehow cached, which gave me an headache. Apparently dumping autoload should rebuild this cache.
Edit: With your newest edit you will not need to do this change as you have changed the migration from creating a blog table to creating a blogs table.
You will need to edit your model to understand that you use non-standard Laravel formatting for your table name.
Laravel, per default, will assume your table is a pluralized version of your model name. To override this, you need to add the following attribute to the model: protected $table = 'blog';
See below code; however, I suggest not changing this and sticking to the Laravel defined standards. The closer you are to doing it the way the framework wants, the easier it will be for you. Especially when learning, it is much easier not to fight the framework at the same time.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Blog extends Model
{
use HasFactory;
protected $table = 'blog';
protected $fillable = [
'title',
'body',
'user_id',
];
}
Related
please how can i change the default model 'role' by another model in my project (laravel project) has the same architecture as 'role'; the default model package.
in the config/permission.php config file contains:
'models' => [
/*
* When using the "HasPermissions" trait from this package, we need to know which
* Eloquent model should be used to retrieve your permissions. Of course, it
* is often just the "Permission" model but you may use whatever you like.
*
* The model you want to use as a Permission model needs to implement the
* `Spatie\Permission\Contracts\Permission` contract.
*/
'permission' => Spatie\Permission\Models\Permission::class,
/*
* When using the "HasRoles" trait from this package, we need to know which
* Eloquent model should be used to retrieve your roles. Of course, it
* is often just the "Role" model but you may use whatever you like.
*
* The model you want to use as a Role model needs to implement the
* `Spatie\Permission\Contracts\Role` contract.
*/
'role' => Spatie\Permission\Models\Role::class,
],
i want to have something like this:
'role' => App\Fonction::class,
the documentation says that i have to implement the
Spatie\Permission\Contracts\Role` contract.
any idea how can i do this in the right way.
the Fonction Model :
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Fonction extends Model
{
protected $fillable = [
'nom','description'
];
public function comptes()
{
return $this->hasMany('App\Compte') ;
}
}
You can extend Spatie\Permission\Models\Role class. It already implements Spatie\Permission\Contracts\Role interface. Check details in doc.
<?php
namespace App;
use Spatie\Permission\Models\Role;
class Fonction extends Role
{
protected $fillable = [
'nom','description'
];
public function comptes()
{
return $this->hasMany('App\Compte') ;
}
}
Could you help me understand the right way to extend existing models? I'm developing a package and want to do as much as possible separated from the main application.
I want to have all existing functionality of the User model, but only add a relation to another model. In my package User I can have several (hasMany) Article. Instead of adding a method to \App\User I created a new model in my package class User extends \App\User:
namespace Package\Sample;
use Laravel\Passport\HasApiTokens;
use Illuminate\Notifications\Notifiable;
/**
* Class User
* #package Package\Sample
*/
class User extends \App\User
{
use HasApiTokens, Notifiable;
public function articles()
{
return $this->hasMany(Article::class);
}
}
To make it work I add the configuration for my package auth.php:
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => \Package\Sample\User::class,
],
],
'guards' => [
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
],
My question: Is it a "best practice" to extend functionality of existing models?
I solved it by using the class_alias() php function.
In my package service provider I setup an alias of the model class defined for Authentication in /config/auth.php like this:
public function boot(){
class_alias(config("auth.providers.users.model"), 'ParentModel');
}
then I use ParentModel class where needed:
use ParentModel;
class Agent extends ParentModel {
...
}
Hope It makes sense for someone
that will prevent edits on the \Package\Sample\User::class. any new method needed will result in a package update.
why not declare a trait in your package containing your methods and use them in the App\User::class like what laravel is using.
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use Notifiable;
I tried using the class_alias and although it works for basic usage, when things got more complicated my user class couldn't cut it. For example, my notifications where using the local package user type and not showing in app.
After doing more research I found using a trait IS the proper way like others have mentioned.
I found a comprehensive guide here: https://laravelpackage.com/08-models-and-migrations.html#approach-2-using-a-polymorphic-relationship
the gist:
Create a trait in your package:
Important here you can setup whatever the relationship you need depending on you db modeling.
// 'src/Traits/HasPosts.php'
<?php
namespace JohnDoe\BlogPackage\Traits;
use JohnDoe\BlogPackage\Models\Post;
trait HasPosts
{
public function posts()
{
return $this->morphMany(Post::class, 'author');
}
}
Add the use in whatever user classes it applies to in your app
// 'App\Models\User.php'
<?php
namespace App\Models;
use JohnDoe\BlogPackage\Traits\HasPosts;
class User extends Authenticatable
{
use HasPosts;
...
Lastly, you'll need to add an extra field in the db for the user_type where you are using the user_id.
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddUserTypeToPostTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::table('posts', function (Blueprint $table) {
$table->tinyText('user_type')
->comment('User class type')
->nullable();
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::table('posts', function (Blueprint $table) {
$table->dropColumn('user_type');
});
}
}
You need to Add config(['auth.providers.users.model' => Myname\Myproject\App\Models\User::class]); to the boot-method inside my package-service-provider.
And Create new Class in your package.
namespace Myname\Myproject\App\Models;
class User extends \App\User
{
public function roles(){
return $this->belongsToMany('Myname\Myproject\App\Models\Role', 'user_role', 'user_id', 'role_id');
}
}
I have a model Test as follows
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Test extends Model
{
use SoftDeletes;
protected $dates = ['deleted_at'];
public function __construct() {
if (!\App::environment('production')) {
$this->table='test_stage';
}
}
I have made sure that there is a 'deleted_at' column in my test_stage table. But the soft deletes are not working. Using the delete() method permanently removes the record from the table. As an additional step of verification I manually added 'deleted_at' value for some columns. But query the model still gives me the soft deleted record.
Moreover, removing the model constructor entirely and simply defining the table name using:
protected $table = 'test_stage';
Works like a charm! That is soft deletes magically start working again.
Or is there any way around to define the table name according to the environment without the need of defining a constructor?
I think the problem could be that you're overwriting the constructor, which is set in Illuminate\Database\Eloquent\Model. Have you tried
public function __construct(array $attributes = []) {
parent::__construct($attributes);
if (!\App::environment('production')) {
$this->table='test_stage';
}
}
Edit: more detailed explaination
As you overwrite the constructor of the class you're extending, the original does not get executed anymore. This means necessary functions for the eloquent model do not get executed. See the constructor for Illuminate\Database\Eloquent\Model below:
/**
* Create a new Eloquent model instance.
*
* #param array $attributes
* #return void
*/
public function __construct(array $attributes = [])
{
$this->bootIfNotBooted();
$this->syncOriginal();
$this->fill($attributes);
}
By making sure the extending class requires the same parameters for the constructor as the extended class and executes parent::__construct($attributes); first, the constructor of the extended class gets executed first. After which you can overwrite $this->table in the extending class.
I'm still quite new to Laravel, Eloquent and Artisan.
What I'm trying to do is pretty easy: I want to create a new Eloquent model AboutUs, along with a migration file to create the table about_us.
I run the following command:
PHP artisan make:model AboutUs -m
This generates the model and migration file, however, the migration file is named '2017_07_18_211959_create_about_uses_table.php', automatically adding the unnecessary 'es' to 'us', and creating a table 'aboutuses' instead of 'about_us'.
If I manually change the migration file like so:
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateAboutUsTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('about_us', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->boolean('active');
$table->string('title')->nullable();
$table->text('text')->nullable();
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('about_us');
}
}
The model like this:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class AboutUs extends Model
{
protected $fillable = ['id', 'active', 'title', 'text'];
public static function getAboutUs()
{
return AboutUs::find(1);
}
public function postAboutUs($session, $active, $title, $text)
{
$aboutUs = $session->get('about_us');
array_push($aboutUs, ['active' => $active, 'title' => $title, 'text' => $text,]);
$session->put('about_us', $aboutUs);
}
}
Then run the migration:
PHP artisan migrate
The database table 'about_us' is created correctly, but when I insert a row in the table and attempt to use getAboutUs, it crashes, the laravel.log stating that:
local.ERROR: exception 'PDOException' with message 'SQLSTATE[42S02]: Base table or view not found: 1146 Table 'ID226233_db.aboutuses' doesn't exist' in C:\PHP Projects\xxx\vendor\doctrine\dbal\lib\Doctrine\DBAL\Driver\PDOConnection.php:77
I can see that there are still references to "aboutuses" in the autoload_classmap and autoload_static files. Changing this manually doesn't fix the issue, nor does running:
composer dump autoload
Next, I tried to simply not rename the table, but run the migration to create the initial "aboutuses" table. This fixed the functionality, as the model now works correctly. However, if I now add a new migration with:
Schema::rename('aboutuses', 'about_us');
This renames the table in the DB, but not in the autoload files or wherever else, resulting in broken functionality.
Surely there must be an easier way to either:
create a model with migration file with a FIXED name, instead of it
automatically changing the name by adding an unnecessary suffix.
rename a model and change the necessary files to prevent the model
from breaking.
Could anyone point me in the right direction before I lose my mind over this? :)
You can specify a custom table name in your Eloquent model class. Here is the example from the docs:
<?php namespace App;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
/**
* The table associated with the model.
*
* #var string
*/
protected $table = 'my_flights';
}
Source: https://laravel.com/docs/5.4/eloquent#eloquent-model-conventions
Hope that helps.
Fairly new to Laravel and would like to follow the table conventions I'm used to.
The default table name for accounts is "users" and I'd like to change it to "account." If anything, I'd like to change it to "user" and remove the plural.
I've already used migrate to make a cloned table of users called "account" and I'm just trying to figure out what all I have to do to existing code to make it work for logging in
It looks like I'll have to somehow update "app/Http/Auth/AuthController.php", but I'm not quit sure what it is I'll have to do...
Do I need to:
Update "use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;"?
Update in AuthController "returnUser::create" to "returnAccount::create"? If so do I need to go somewhere else in the code where that class User is being created?
I guess another option is just scrapping their AuthController and establishing my own and just calling a new object of Account... Is this the route I should take?
I would simply extend the User class and overrule some things if you want to have the Model named Account:
Edit the table property in the Account class, see: https://github.com/laravel/laravel/blob/master/app/User.php#L24
Account extends User {
protected $table = 'accounts';
}
Once your class Account is created edit the configured authentication class, see:
https://github.com/laravel/laravel/blob/master/config/auth.php#L31
If you only want to overrule the table used by User, edit the User class:
protected $table = 'accounts';
To be honest, why bother? Taylor provided this skeleton for you to kickstart your application, why not use that especially if you're new to Laravel?
Firstly, create account(s) migration - plural is widely acceptable
Migration must contain all important fields
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateAccountsTable extends Migration
{
public function up()
{
Schema::create('accounts', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('email')->unique();
$table->string('password', 60);
$table->rememberToken();
$table->timestamps();
});
}
public function down()
{
Schema::drop('accounts');
}
}
Then create Account model,
<?php namespace App;
use Illuminate\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Auth\Passwords\CanResetPassword;
use Illuminate\Foundation\Auth\Access\Authorizable;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
class Account extends Model implements AuthenticatableContract,
AuthorizableContract,
CanResetPasswordContract
{
use Authenticatable, Authorizable, CanResetPassword;
protected $table = 'accounts';
protected $fillable = ['name', 'email', 'password'];
protected $hidden = ['password', 'remember_token'];
}
head over to config\auth.php and change this line:
'model' => App\User::class,
to
'model' => App\Account::class,