laravel model factory override default state - php

Is it possible to override the defaults of a factory without using states?
I use a tool that generates the factories from my models, but I would like to modify some attributes. I know that I can use
$factory->state(\App\User::class, 'moderator', function ...
but I would like to do it without depending on specifying the state with every model creation. So something like
$factory->state(\App\User::class, 'default', function ...

What you define in the factory is the default behaviour, for example
$factory->define(App\User::class, function(Faker $faker) {
return [
...
'name' => 'Jon Snow',
...
];
});
You can override this default behaviour with a state, for example
$factory->state(App\User::class, 'bad-guy', function (Faker $faker) {
return [
'name' => 'Night King'
]
};
And the last override you can do, is when you want to create that instance, for example
$jonSnow = factory(App\User::class)->create();
$nightKing = factory(App\User::class)->states('bad-guy')->create();
$samTarly = factory(App\User::class)->create([
'name' => 'Sam Tarly'
]);

Related

Laravel Livewire - Constant expression contains invalid operations in $rules property

I tried to define some validation rules in my livewire component to validate some FormData:
protected $rules = [
'website' => 'url|nullable',
'zipcode' => 'regex:/\b\d{5}\b/|nullable',
'founding_year' => 'required|digits:4|integer|min:1700|max:2020',
];
That work's very well until I need to validate against the value of a variable or a dynamic value in general.
E.g.: Changing the max property from hardcoded 2020 to the current year:
protected $rules = [
...
'founding_year' => 'required|digits:4|integer|min:1700|max:'. date('Y'),
];
Unfortunatelly this resolves in an exception:
Symfony\Component\ErrorHandler\Error\FatalError
Constant expression contains invalid operations
Has someone an idea how to fix this?
You can't call functions or methods when declaring the value of a property directly in PHP.
With Livewire, you can specify a rules() method which returns the rule-array instead - this allows you to use functions in the rules. Internally, Livewire will now run the result of that method instead of grabbing the protected $rules array. This means you can still hook into the $this->validate() and $this->validateOnly() methods Livewire ships with.
So instead of defining your protected $rules; attribute, declare the rules() method,
public function rules()
{
return [
'website' => 'url|nullable',
'zipcode' => 'regex:/\b\d{5}\b/|nullable',
'founding_year' => 'required|digits:4|integer|min:1700|max:'.date("Y"),
];
}

Laravel Backpack - Set extra attributes before page save

In my PageTemplates.php I have a field like this:
$this->crud->addField([
'name' => 'adres',
'label' => 'Adres',
'type' => 'address',
'fake' => true,
]);
Now I would like to save also the latitude and longitude of the address they give in (if it can be found). I've copied the PageCrudController and changed the config in config/backpack/pagemanager.php to:
return [
'admin_controller_class' => 'App\Http\Controllers\Admin\PageCrudController',
'page_model_class' => 'App\Models\Page',
];
In my store function I have:
public function store(StoreRequest $request)
{
$address = $request->request->get('adres');
$addressObj = app('geocoder')->geocode($address)->get()->first();
if($addressObj)
{
}
$this->addDefaultPageFields(\Request::input('template'));
$this->useTemplate(\Request::input('template'));
return parent::storeCrud();
}
But what do I place in the if statement? How can I add (= set) an extra field to the extras field in my database?
In backpack 4.1, I solved my issue by the following way :
Override the store method in my controller, set my extra field in request and then call the backpack store method
Don't forget to add include backpack trait
Hope the solution will help someone
use \Backpack\CRUD\app\Http\Controllers\Operations\CreateOperation { store as traitStore; }
public function store()
{
$this->crud->setOperationSetting('saveAllInputsExcept', ['save_action']);
$this->crud->getRequest()->request->add(['updated_by' => backpack_user()->id]);
return $this->traitStore();
}
Fixed it by doing the following:
Add latitude and longitude as hidden fields:
$this->crud->addField([
'name' => 'latitude',
'type' => 'hidden',
'fake' => true,
]);
$this->crud->addField([
'name' => 'longitude',
'type' => 'hidden',
'fake' => true,
]);
Set attributes by doing the following:
if($addressObj)
{
$request['latitude'] = $addressObj->getCoordinates()->getLatitude();
$request['longitude'] = $addressObj->getCoordinates()->getLongitude();
}
}
Change parent::updateCrud to parent::updateCrud($request);.
For people still looking at this issue, I'd recommend you follow the advice in the note under the Callbacks section of Laravel Backpack's docs if you don't just want to observe changes made from the Backpack admin panel, you just need to create an Observable.
To do this you can do the following:
Create an Observer class: php artisan make:observer YourObserver --model=YourModel
Add your code to the generated event methods you wish to observe.
Register the Observer by calling the observe method on the model you wish to observe in your EventServiceProvider's boot method like so:
public function boot()
{
YourModel::observe(YourObserver::class);
}
Or equally you can register the Observer to the $observers property of your applications' EventServiceProvider class:
protected $observers = [
YourModel::class => [YourObserver::class],
];

Yii2 Custom ActionFilter not working with "only" defined as wildcard

I have:
RestModule > TargetController extends BaseController
in BaseController:
public function behaviors()
{
$behaviors['myfilter'] = [
'class' => MyFilter::className(),
'only' => ['rest/target/*'],
];
return $behaviors;
}
but my filter working until "only" is not set or if I set TargetController actions names using "except"
Yii2 versin is 2.0.11.2 on php 5.5 debian8
Since version 2.0.9 action IDs can be specified as wildcards, e.g. site/*. yii2-doc
If you want to attach filter with 'only' property and put IDs as wildcard, e.g. target/*, you should attach it as behavior to module class, not controller.
Try this in your RestModule:
RestModule:
public function behaviors()
{
$behaviors['myfilter'] = [
'class' => MyFilter::className(),
'only' => ['target/*'],
];
return $behaviors;
}

Laravel 5 - Use variables of model in model factories

Okay, let's say that I have a Post model with the attributes name, slug and content. I'd like to generate models with my ModelFactory, but want to set a specific name, which I do by overwriting the value:
factory(App\Post::class)->create(["name" => "Something here"]);
But now I want the slug to auto-generate by using the (new) name and without passing it as argument. Like
"slug" => str_slug($name);
Is this possible or do I need to write the slug manually?
When using the factory below with ->create(['name' => 'anything']); the slug is not created.
My current factory
$factory->define(App\Post::class, function (Faker\Generator $faker) {
static $name;
return [
'name' => $faker->name,
'slug' => $name ?: str_slug($name),
'content' => $faker->sentences(),
];
});
This should do the trick. You can pass a name in manually or let Faker handle it.
$factory->define(App\Post::class, function (Faker\Generator $faker) {
return [
'name' => $faker->name,
'slug' => function (array $post) {
return str_slug($post['name']);
},
'content' => $faker->sentences(),
];
});
Have you tried
$name ="Something here";
factory(App\Post::class)->create(["name" => $name, "slug" => str_slug($name)]);

Laravel model inheritance

I am using the Toddish/Verify library for Laravel as it includes 99% of what I need for my project. All I need is to add some fields.
I have added them in a migration, and I want to add them also to mass creation:
use Toddish\Verify\Models\User as VerifyUser;
class User extends VerifyUser
{
public function __construct () {
array_merge ($this->fillable, array(
'salutation', 'title', 'firstname', 'lastname', 'phonenumber', 'mobilenumber'
));
}
}
However, when I run my creation test:
public function testUserCreation () {
$user = User::create(
[
'username' => 'testusername',
'email' => 'email#test.com',
'password' => 'testpassword',
'salutation' => 'MrTest',
'title' => 'MScTest',
'firstname' => 'Testfirstname',
'lastname' => 'Testlastname',
'phonenumber' => 'testPhoneNumber',
'mobilenumber' => 'testMobileNumber',
]
);
$this->assertEquals($user->salutation, 'MrTest');
$this->assertEquals($user->title, 'MScTest');
$this->assertEquals($user->firstname, 'Testfirstname');
$this->assertEquals($user->lastname, 'Testlastname');
$this->assertEquals($user->phonenumber, 'testPhoneNumber');
$this->assertEquals($user->mobilenumber, 'testMobileNumber');
}
I get this:
Illuminate\Database\QueryException: SQLSTATE[23000]: Integrity constraint violation: 19 users.username may not be NULL (SQL: insert into "users" ("updated_at", "created_at") values (2014-03-03 09:57:41, 2014-03-03 09:57:41))
in all tests that involve user creation, as if it had forgotten about the parents attributes when saving the model.
What am I doing wrong?
The problem is that you're overriding what I assume is the Eloquent constructor, so the values are never getting passed.
Change __construct to look like the following.
public function __construct(array $attributes = array())
{
parent::__construct($attributes);
array_merge ($this->fillable, array(
'salutation', 'title', 'firstname', 'lastname', 'phonenumber', 'mobilenumber'
));
}
The Model::create method will actually create a new instance of the model and pass the array into the __construct. You're overriding this and preventing it from passing the information through.
Note If you decide to override core methods like you've done here, always check inheritance and make sure you aren't breaking anything.

Categories