php - Laravel framework MassAssignmentException During Seed Data - php

I have got following issue during seed data in laravel framework.
php artisan db:seed --class=LevelsTableSeeder
[Illuminate\Database\Eloquent\MassAssignmentException]
level
My seed file(LevelsTableSeeder.php) as follows.
<?php
use App\Models\Levels;
use Illuminate\Database\Seeder;
class LevelsTableSeeder extends Seeder {
public function run()
{
$levels = [
['level'=>1, 'xp_second'=>0.101, 'xp_hour'=>365.220],
['level'=>2, 'xp_second'=>0.104, 'xp_hour'=>365.220]
];
foreach($levels as $level) {
Levels::create($level);
}
$this->command->info('Levels seeded :-)');
}
}
And my Levels model file as follows:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Cache;
class Levels extends Model {
protected $table = 'levels';
public function maxLevel()
{
return Cache::get('levels_max_level', function() {
return parent::max('level');
});
}
}
Could you help me?
Regards.

The MassAssignmentException is thrown whenever you try to assign multiple fields in a Model and then create it in a database. In order to protect your database, the Eloquent library offers you the Mass Assignment Protection, which means only fields you allow to be mass assigned will be accepted.
Laravel 5.2 Documentation for Eloquent
Add to your Model the attribute protected $fillable with an array of fields you are interested in mass assigning:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Cache;
class Levels extends Model {
protected $table = 'levels';
protected $fillable = ['level', 'xp_second', 'xp_hour'];
public function maxLevel()
{
return Cache::get('levels_max_level', function() {
return parent::max('level');
});
}
}
Another way to solve this issue is to unguard your model by calling Levels::unguard() from your Seeder Class. That way you will disable the Guard that protects your model from being mass assigned.
<?php
use App\Models\Levels;
use Illuminate\Database\Seeder;
class LevelsTableSeeder extends Seeder {
public function run()
{
Levels::unguard(); // to unguard your model
$levels = [
['level'=>1, 'xp_second'=>0.101, 'xp_hour'=>365.220],
['level'=>2, 'xp_second'=>0.104, 'xp_hour'=>365.220]
];
foreach($levels as $level) {
Levels::create($level);
}
$this->command->info('Levels seeded :-)');
Levels::guard(); // To guard back
}
}
This is an acceptable solution for seeding purposes, but not recommended on controller actions.

You should read the Laravel Docs, Laravel provides protection from Mass Assignment vulnerabilities and so you actually have to tell Laravel which fields are mass assignable inside your model, using the $fillable property:
class Levels extends Model {
protected $fillable = ['level', 'xp_second', 'xp_hour'];
}

Related

Laravel 8 – Model instance error - too few arguments to function __construct()

I’m kind of new to Laravel and the whole API architecture, so my question may seem dumb at first.
My basic setup:
Laravel 8;
PHP 8;
routes\api.php
Route::post('/categories/',[ApiCategoriesInsertController::class, 'insertCategories'], function($insertCategoriesResults) {
return response()->json($insertCategoriesResults);
})->name('api.categories.insert');
\app\Http\Controllers\ApiCategoriesInsertController.php (created with php artisan make:controller)
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
// Custom models.
use App\Models\CategoriesInsert;
class ApiCategoriesInsertController extends Controller
{
private mixed $ciAPI;
public function __construct(Request $req)
{
}
public function insertCategories(Request $req): array
{
$this->ciAPI = new CategoriesInsert(['testing'=>'debug']);
return [‘status’ => ‘OK’];
}
}
\app\Models\CategoriesInsert.php (created with php artisan make:model)
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class CategoriesInsert extends Model
{
use HasFactory;
public function __construct(array $objParameters)
{
}
}
When I make a post to http://localhost:8000/api/categories, Laravel logs the following error:
local.ERROR: Too few arguments to function App\Models\CategoriesInsert::__construct(), 0 passed in … Too few arguments to function App\\Models\\CategoriesInsert::__construct(), 0 passed in …
Anyone knows what’s wrong or missing in my architecture?
Thanks!
Make your model's constructor compatible with parent.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class CategoriesInsert extends Model
{
use HasFactory;
public function __construct(array $attributes = [])
{
parent::__construct($attributes);
}
}
Also, notice, that when you call new CategoriesInsert(['testing'=>'debug']), you do not save the data in your database. Use:
$insert = new CategoriesInsert(['testing'=>'debug']);
$insert->save();
Or:
CategoriesInsert::create(['testing'=>'debug']);
you don't need to pass data to model
change your code to bellow code:
public function insertCategories(Request $req): array
{
CategoriesInsert::create(['testing' => 'debug']);
return [‘status’ => ‘OK’];
}
key of passed array is your filed name in database, and value stored data
also you should define fillable parameter in model
protected $fillable = [
'title',
'slug',
'priority',
];

get data from model with condition laravel 5.7

I have Users_group model in my Laravel project.
Code:
<?php
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Database\Eloquent\Model;
class Users_group extends Model
{
use Notifiable;
protected $table = 'users_groups';
protected $fillable = ['id', 'user_id', 'group_id', 'created_at', 'updated_at'];
public function getGroupData()
{
return $this->hasOne('App\Group','id','group_id');
}
}
And when I want to get data from this model I use this my custom method:
$data = App\Users_group ::get();
It's working great
I want to get the data from model with condition not like this:
$data = App\Users_group::where('group_id','>',5)->get();
I need to put the condition inside the model file thats make the model return the data everytime I call User_group::get() return it when condition inside the model.
Thanks!
You can use query scopes to achieve this. Query scopes allow you to add a global constraint to a model.
In your case, you could add the following boot method to your model:
protected static function boot()
{
parent::boot();
static::addGlobalScope('groupOver5', function (Builder $builder) {
$builder->where('group_id', '>', 5);
});
}
try this one in by using scope in model
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Database\Eloquent\Model;
class Users_group extends Model
{
use Notifiable;
protected $table = 'users_groups';
protected $fillable = ['id', 'user_id', 'group_id', 'created_at', 'updated_at'];
public function scopeGetGroupData($query,$value,$opertor = '>')
{
return $query->where('group_id', $opertor, $value);
}
}
get data in controller like this u can pass also parameter in method
App\Users_group::GetGroupData($value)->get();

Laravel don't seed extended models

I create alternative Id filed called alterId in database and use it in some cases instead of id field.
For example, on edit data client send alterId value instead of id...
Because it's part of a code that often repeats itself I extended Illuminate\Database\Eloquent\Model class with this class:
namespace App\NewModel;
use Illuminate\Database\Eloquent\Model;
class NewModel extends Model
{
public function __construct()
{
parent::__construct();
$this->attributes['alterId'] = $this->generateAlterId();
}
public static function convertIdToAlterId($alterId)
{
// some logic to convert if to alterId
}
public static function convertAlterIdToId($id)
{
// some logic to alterId to id
}
public function generateAlterId($tokenSize = 32)
{
// logic to generate alter id ftom time stamp or something like that...
}
}
that generate alterId automatically and has possibility of converting from one to the other.
My problem is when I try to seed some element with this extended model nothing happens, no errors and no inserted data in database.
For seeding I use this model
namespace App;
use Illuminate\Database\Eloquent\Model;
use \App\NewModel;
class TestModel extends NewModel
{
protected $fillable = ['name'];
protected $dates = ['created_at', 'updated_at'];
}
this simple factory:
use Faker\Generator as Faker;
$factory->define(App\TestModel::class, function (Faker $faker) {
return [
'name' => $faker->firstNameMale,
'created_at' => \Carbon\Carbon::now(),
'updated_at' => \Carbon\Carbon::now()
];
});
and seeder:
use Illuminate\Database\Seeder;
use \App\TestModel;
class TestModelsTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* #return void
*/
public function run()
{
//truncate
TestModel::truncate();
//create
factory(TestModel::class, 100)->create();
}
}
As I said before, nothing happens when I start the command
php artisan migrate:refresh --seed
even a error not showed. But if I change code and modify TestModel to inherits the Model instead of the NewModel then everything works fine, database get inserts!
What did I do wrong in this code?
I haven't tried to run your code, but I think you need to make it fillable:
protected $fillable = ['name','alterId'];

Load extra table/data into user object

In Laravel 5.6 I'm trying to load data into the user object, so I can view user credentials/settings etc.
Whats annoying is I had it working, but for some reason now It seems to have stopped, and I'm not sure what I've changed to break it.
Anyway I want to load two tables, access and settings. Both of them have user_id field in there with the corresponding user_id in.
In my User.php class I have two functions:
public function access() {
return $this->hasMany(Access::class);
}
public function settings() {
return $this->hasOne(Settings::class);
}
I am not Use-ing them at the top of the class (i.e. use \App\Access) if that makes any difference.
And then the Access class looks like:
namespace App;
use Illuminate\Database\Eloquent\Model;
class Access extends Model
{
protected $table = "access";
}
And the Settings class is very much the same:
namespace App;
use Illuminate\Database\Eloquent\Model;
class Settings extends Model
{
protected $table = "settings";
}
However whenever I try and access Auth::user()->settings or Auth::user()->access I get undefined index: error. It's frustrating because like I said I had it working the other day and I'm not sure what's changed.
Few things you could try here. First, Lazy Eager Load the relationships by loadMissing:
// settings
Auth::user()->loadMissing('settings');
// access
Auth::user()->loadMissing('access');
To load a relationship only when it has not already been loaded, use the loadMissing method
Second, you can use with when querying for a user, although it's not as relevant with using the auth facade:
User::with(['settings', 'access'])->where('atribute', $value)->get();
Last, if you always want the settings and access relationships to always be returned with each user model, set the with attribute on the user model:
public class User {
protected $with = ['settings', 'access'];
...
}
I usually define the inverse relationships on models as well, so Access and Settings would have a BelongsTo relationship defined:
class Access extends Model
{
protected $table = "access";
public function user() {
return $this->belongsTo(User::class);
}
}
class Settings extends Model
{
protected $table = "settings";
public function user() {
return $this->belongsTo(User::class);
}
}

Laravel 5.5 MassAssignmentException

I'm following the Laravel From Scratch tutorial series, I'm currently at the part that you are creating a comment system for your articles system. But I'm having a problem, I don't really know what the error is saying at this point.
The error:
Illuminate\Database\Eloquent\MassAssignmentException
body
The comment model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Comment extends Model
{
public function post()
{
return $this->belongsTo(Post::class);
}
}
The post model:
<?php
namespace App;
class Post extends Model
{
public function comments()
{
return $this->hasMany(Comment::class);
}
public function addComment($body)
{
$this->comments()->create(compact('body'));
}
}
The route I made:
Route::post('/posts/{post}/comments', 'CommentsController#store');
The comments controller:
<?php
namespace App\Http\Controllers;
use App\Post;
class CommentsController extends Controller
{
public function store(Post $post)
{
$post->addComment(request('body'));
return back();
}
}
Thanks in advance!
Explanation of this error
This is a security feature of Laravel. It is designed to protect you against form manipulation when using mass assignments.
For example on a sign-up form: When you have an is_admin column in your database, a user simply could manipulate your form to set is_admin to true on your server, and therefore in your database. This security feature prevents that by using a whitelist to define safe fields.
How to fix that
You need to set a $fillable property on your model. It's value must be an array containing all fields that are safe to mass assignable (like username, email address, ...).
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Comment extends Model
{
# This property!
protected $fillable = ['body'];
// ...
}
See "Mass assignment" in the docs:
https://laravel.com/docs/5.5/eloquent#mass-assignment
Mass assignment is when you send an array to the model creation, basically setting a bunch of fields on the model in a single go, rather than one by one, something like what you did here:
public function addComment($body)
{
$this->comments()->create(compact('body'));
}
You need to add the field you are populating to the fillable array in Comments.php model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Comment extends Model
{
protected $fillable = ['body'];
public function post()
{
return $this->belongsTo(Post::class);
}
}
As the documentation states:
You may also use the create method to save a new model in a single
line. The inserted model instance will be returned to you from the
method. However, before doing so, you will need to specify either a
fillable or guarded attribute on the model, as all Eloquent models
protect against mass-assignment by default.
Hope this helps you.

Categories