Laravel Ardent many-to-many relationship is throwing error - php

I have an Ardent v3.3.0 model like this:
class Company extends Ardent
{
protected $fillable = [
'name',
'address'
];
public static $rules = [
'name' => 'required',
'address' => 'string'
];
public function workers()
{
return $this->belongsToMany('App\Worker')->withPivot('worker_type');
}
}
Another model that goes something like this:
class Worker extends Ardent
{
protected $fillable = [
'name',
];
public static $rules = [
'name' => 'required',
];
public function company()
{
return $this->belongsToMany('App\Company')->withPivot('worker_type');
}
}
In my controller I'm calling a save method like this:
$worker = New Worker;
$worker->name = "Jane Smith";
$company = Company::find($id);
$company->workers()->save($worker, ['worker_type' => 'contractor'];
According to Laravel's and Ardent's docs I'm correctly forming my relationships but Ardent throws an error: ErrorException in Ardent.php line 821: Invalid argument supplied for foreach()
What's causing this error? Is it a bug in my code or Ardent's?
Note: I'm using Ardent version 3.3.0, the issue disappears when I roll back to version 3.0.0

You haven't defined the rules for one or more of your models that extend Ardent.
class Whatever extends \LaravelArdent\Ardent\Ardent {
public static $rules = [];
}
Line 821 shows that we're looking for an array called $ruleset that we can't find.

Related

eloquent relations with extend user model sentinel laravel package

I have a class User that extends
<?php
namespace App;
class User extends \Cartalyst\Sentinel\Users\EloquentUser
{
public function chalets(){
return $this->hasMany('App\Chalet');
}
}
and i have Chalet Class
class Chalet extends Model
{
protected $fillable = [
'name', 'description',
];
public function user(){
return $this->belongsTo('App\User');
}
}
And i have method to add chalet by user :
public function postCreateChalet(Request $request){
$chalet = new Chalet([
'name' => $request->input('name'),
'description' => $request->input('description')
]);
Sentinel::getUserRepository()->setModel('App\User');
$user = Sentinel::getUser();
$user->chalets()->save($chalet);
return ('chalet has created');
}
and its give me an error :
BadMethodCallException
Call to undefined method Cartalyst\Sentinel\Users\EloquentUser::chalets()
Is it a right way to extend User class ?
I have searched for ways to extend the User class. I found this question:Model Inheritance in Laravel didn't help me though.
I'm using Laravel 5.7
The exception you're getting indicates Sentinel is still referring to the default stock Sentinel's EloquentUser model. Make sure you point to your extended user model with the published Sentinel configurations.
Run the below command
php artisan vendor:publish --provider="Cartalyst\Sentinel\Laravel\SentinelServiceProvider"
Open up the published config file at 'config\cartalyst.sentinel.php'
Modify it from the below content:
'users' => [
'model' => 'Cartalyst\Sentinel\Users\EloquentUser',
],
to:
'users' => [
'model' => 'App\User',
],
For more information, refer to https://github.com/cartalyst/sentinel/wiki/Extending-Sentinel
You won't need the following line after you configured it via config:
Sentinel::getUserRepository()->setModel('App\User');

Laravel Custom Model Class - Field Does not have a Default Value

So, I have a custom Model extension class called RecursiveModel:
use Illuminate\Database\Eloquent\Model;
use ... RecursiveHelper;
class RecursiveModel extends Model {
private $recursiveHelper = null;
public function __construct(){
$this->recursiveHelper = new RecursiveHelper();
parent::__construct();
}
public function save(array $options = []){
parent::save($options);
}
...
// Additional methods available for Recursive Models (self-referenced `parent_id` relationships)
}
And, a Model that extends this RecursiveModel class instead of the base Model class:
use ... RecursiveModel;
use Illuminate\Database\Eloquent\SoftDeletes;
class Line extends RecursiveModel {
use SoftDeletes;
protected $table = "lines";
protected $primaryKey = "id";
public function parent(){
return $this->belongsTo(self::class, "parent_id", "id");
}
public function children(){
return $this->hasMany(self::class, "parent_id", "id");
}
}
All is well and good, and with previously imported records (back when Line extended Model and not RecursiveModel, I was able to use my RecursiveHelper methods/logic without issue. Now, I'm trying to refresh my database, which calls a Seeder:
use Illuminate\Database\Seeder;
use ... Slugger;
use ... Line;
class LinesSeeder extends Seeder {
public function run(){
$parentLine = Line::create([
"name" => "Line Item",
"slug" => $this->slugger->slugify("Line Item"),
"created_at" => date("Y-m-d H:i:s"),
"updated_at" => date("Y-m-d H:i:s"),
]);
$childLine = Line::create([
"name" => "Child Line Item",
"slug" => $this->slugger->slugify("Child Line Item"),
"parent_id" => $parentLine->id,
"created_at" => date("Y-m-d H:i:s"),
"updated_at" => date("Y-m-d H:i:s"),
]);
...
}
}
As previously stated, when Line extended Model and not RecursiveModel, this code worked without issue. But now, I'm running into this error:
SQLSTATE[HY000]: General error: 1364 Field 'name' doesn't have a default value (SQL: insert into lines
(updated_at, created_at) values (2018-08-13 15:56:45, 2018-08-13 15:56:45))
The Line::create([...]); doesn't seem to be receiving the parameter passed; is there something I'm missing when extending Model.php? I've tried adding:
public function create(array $options = []){
parent::create($options);
}
To RecursiveModel, but that just throws another error (and I don't think the create() method is a part of Model.php, but rather Builder.php.)
Also, it's not an issue with protected $fillable, nor is it an issue with setting 'strict' => true, on my mysql connection; already tried both of those to no avail.
As suggested, updated __construct method of RecursiveModel to:
public function __construct(array $attributes = []){
$this->recursiveHelper = new RecursiveHelper();
return parent::__construct($attributes);
}
Unfortunately, still getting the same error.
Edit: Line.php had a __construct method that was carried over from when I was applying $this->recursiveHelper model by model; solution was to update signature to match (as noted above) or remove __construct from extending models.
Model constructors need to take in an array of attributes:
public function __construct(array $attributes = [])

Laravel 5.1 How to use the same form request rules for two methods when a field is required in one but not in the other method?

I have a form with 3 file input fields and the user should upload at least one file. I have a form request in which I'm validating them as follows:
public function rules()
{
$this->prepInput();
return [
'comment' => 'max:2000',
'source' => 'different:target',
'file1'=>'required_without_all:file2,file3|between:1,15360|mimes:txt,pdf',
'file2'=>'required_without_all:file1,file3|between:1,15360|mimes:txt,pdf',
'file3'=>'required_without_all:file1,file2|between:1,15360|mimes:txt,pdf'
];
}
To update the same form, I'm using update method in my controller which is almost the same as store method. The only difference is that files are not required in the update form. Is there any way to use the same form request with the store and update methods and apply the required rule optionally?
I'm basically using an abstract base class for both of my requests, and then add any required rules in subclasses. This will preserve DRY and gives a flexible way to add rules. For example:
abstract class CompanyBaseRequest extends FormRequest
{
...
public function rules()
{
return [
'name' => ['required', 'string', 'max:255'],
'category_id' => ['required', 'exists:company_categories,id'],
'short_description' => ['required', 'string', 'max:2000'],
'video' => ['nullable', 'file', 'mimes:mp4', 'max:30000'],
];
}
}
And then two subclasses:
class CompanyStoreRequest extends CompanyBaseRequest
{
...
public function rules()
{
return array_merge(parent::rules(), [
'logo' => ['required', 'file', 'mimes:png,jpg,jpeg', 'max:1024'],
]);
}
}
class CompanyUpdateRequest extends CompanyBaseRequest
{
...
public function rules()
{
return array_merge(parent::rules(), [
'logo' => ['nullable', 'file', 'mimes:png,jpg,jpeg', 'max:1024'],
]);
}
}
You should use one of these subclasses where needed, and they both will contain rules from the base class and rules from themselves.
This is better from the accepted answer because the forms themselves are explicitly saying what they do in their name, and don't just work with one condition (which is not clear what they check).
As you are doing like using a method $this->prepInput(); I suggest you change a little bit code to reuse.
You have to create named route for both the routes create & edit. I assume you are using resourceful routing
Change your code like bellow
public function isEditRequestCalled()
{
return app('router')->getCurrentRoute()->getName() == 'YOUR_EDIT_ROUTE_NAME';
}
and in your request method you change like this
public function rules()
{
$this->prepInput();
return $this->isEditRequestCalled() ? [
//YOUR EDIT RULES GOES HERE
] : [//YOUR CREATE RULES GOES HERE
'comment' => 'max:2000',
'source' => 'different:target',
'file1'=>'required_without_all:file2,file3|between:1,15360|mimes:txt,pdf',
'file2'=>'required_without_all:file1,file3|between:1,15360|mimes:txt,pdf',
'file3'=>'required_without_all:file1,file2|between:1,15360|mimes:txt,pdf'
];
}
I used the following trick and it worked:
public function rules()
{
$this->prepInput();
$rules= [
'comment' => 'max:2000',
'source' => 'different:target',
'file1'=>'required_without_all:file2,file3|between:1,15360|mimes:txt,pdf',
'file2'=>'required_without_all:file1,file3|between:1,15360|mimes:txt,pdf',
'file3'=>'required_without_all:file1,file2|between:1,15360|mimes:txt,pdf'
];
if($this->myprojects){
$rules['file1'] = 'between:1,15360|mimes:txt,pdf';
$rules['file2'] = 'between:1,15360|mimes:txt,pdf';
$rules['file3'] = 'between:1,15360|mimes:txt,pdf';
}
return $rules;
}
Here my route information are as follows:
myprojects/{myprojects}/edit | myprojects.edit | App\Http\Controllers\MyProjectsController#edit
So the id of the my myprojects entity is $this->myprojects. If it is null it is creating a myprojects, if it has a value it is updating the corresponding myprojects.
I use separate Rule classes, which basically just store the $rules and $messages I need for use and reuse in FormRequest classes.
class RulePrep
{
/**
* #var array
*/
public $rules = [];
/**
* #var array
*/
public $messages = [];
}
class RuleProjects
{
/**
* #var array
*/
public $rules = [];
/**
* #var array
*/
public $messages = [];
}
You could try that?
You'd need separate FormRequest classes but it's perhaps neater than all bundled into one with the conditional logic in there.

Laravel Eloquent model overriding static boot method

I want to override model events and found this example code but am not sure I understand it completely.
SOURCE:
http://driesvints.com/blog/using-laravel-4-model-events/
There is a static method with another static method in it...How does that work? Or is it setting a static property in the boot method somehow?
<?php
class Menu extends Eloquent {
protected $fillable = array('name', 'time_active_start', 'time_active_end', 'active');
public $timestamps = false;
public static $rules = array(
'name' => 'required',
'time_active_start' => 'required',
'time_active_end' => 'required'
);
public static function boot()
{
parent::boot();
static::saving(function($post)
{
});
}
}
static::saving() just calls the static method saving on itself (and parent classes if not existent in current class). So it is essentially doing the same as:
Menu::saving(function($post){
});
So it is registering a callback for the saving event within the boot function.
Laravel documentation on model events

Proper implementation for multiple validation

Here's my code:
I'm trying to implement it on ServiceProvider but I don't have any luck.
//Contact.php
class Contact extends \Eloquent {
protected $fillable = array('email', 'name', 'subject', 'msg');
public static $rules = array(
'email' => 'required|email',
'name' => 'required',
'subject' => 'required',
'msg' => 'required'
);
public static function validate($input) {
return Validator::make($input, static::$rules);
}
}
//Registration .php
class Registration extends \Eloquent {
protected $fillable = array('name', 'address', 'birthdate', 'gender', 'civil_status', 'nationality', 'contact_number', 'email', 'invited');
protected $guarded = array('id');
public static $rules = array(
"name" => "required|alpha_spaces",
"address" => "required",
"contact_number" => "required|numeric",
"email" => "required|email|unique:registrations"
);
public static function validate($input) {
return Validator::make($input, static::$rules);
}
}
class HomeController extends BaseController
{
public function postContactForm()
{
return Contact::validate(Input::all());
}
public function postRegistrationForm()
{
return Registration ::validate(Input::all());
}
}
Is the a way that I can implement it like this?
$this->validate-check(Input::all());
I'm trying to refactor my code and also still new using laravel 4 as well.
Thanks,
Aldren,
I think a Service Provider is a bit overkill for this task. You can create something like a Validation Service. Let me explain:
Say you put your custom files under app/src and use composer to autoload the classes there.
Create an abstract Validator class. This way you can extend this class for every model you need to validate:
<?php namespace Foo\Services\Validation;
abstract class Validator {
protected $errors;
public function check($validator)
{
if ($validator->fails()) {
$this->errors = $validator->messages();
return false;
}
return true;
}
public function isValidForCreation($input)
{
$validator = \Validator::make($input, static::$insertRules);
return $this->check($validator);
}
public function isValidForUpdate($input)
{
$validator = \Validator::make($input, static::$updateRules);
return $this->check($validator);
}
public function errors()
{
return $this->errors;
}
}
Now, lets say you want to validate your Contact model input, right ? You can then create a ContactValidator class that extends our Validator abstract class:
<?php namespace Foo\Services\Validation;
class ContactValidator extends Validator
{
static $insertRules = [
'name' => 'required'
];
static $updateRules = [
'name' => 'required'
];
}
All right, so now we have our boilerplate done. Now lets go to ContactsController to implement our new ContactValidator.
First of all, we need to inject our validator inside the controller. IMHO the best way to do it is in the controllers constructor.
So, lets go:
<?php
use Foo\Services\Validation\ContactValidator as Validator;
class ContactsController extends \BaseController {
protected $validator;
function __construct(Validator $validator)
{
$this->validator = $validator;
}
Great! Now we have it injected. Next, we have to make sure our ContactValidator is invoked when I try to store a Contact. Lets say your method is called store().
public function store()
{
if(!$this->validator->isValidForCreation(Input::all()))
{
return Redirect::back()->withErrors($this->validator->errors())->withInput();
}
else
{
//store your data here.
}
}
You can use either $this->validator->isValidForCreation or $this->validator->isValidForUpdate to check your input against the Validator Service.
I hope you can understand everything and if you have any doubts please let me know.
Cheers and good coding :D
Thanks for the input GustavoR, I get what you want to explain right here. But is it possible to implement the $this->validator in one controller?
use Foo\Services\Validation\MainValidator as Validator;
class HomeController extends BaseController
{
protected $validator;
public function __construct(Validator $validator)
{
$this->validator = $validator;
}
public function postContactForm()
{
return ( $this->validator->isValidForCreation(Input::all()) );
}
public function postRegistrationForm()
{
return ( $this->validator->isValidForCreation(Input::all()) );
}
}
Again, thanks for the input :)

Categories