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');
I have a controller that store the data. This use a form Request and after Show the messages on a trait. I am using the same trait on 3 API but i want to know if is posible send/add to the json/array a Custom Message for each API. For example if Category created (Category created successfull o Product Created Successfull) according the api.
This is my Store on my controller
public function store(StoreCategory $request)
{
$data = request()->all();
$newCategory = Category::create($data);
return $this->respondCreated(new CategoryResource($newCategory));
}
I am using CategoryResource like Resource
And this is my trait
public function respondCreated($data)
{
return $this->setStatusCode(IlluminateResponse::HTTP_CREATED)->respond($data
->response()
);
}
public function respond($data, $headers = [])
{
$response = $data;
return $response->setStatusCode($this->statusCode);
}
CategoryResource code:
public function toArray($request) {
return [ 'id' => $this->id,
'name' => $this->name,
'parent_id' => $this->parent_id,
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
];
}
Is Possible add a custom message per Api Request? May be can i pass a Variable from my controller and after add the custom message add the variable to the array?
Best Regards
Sorry my english is not good
You can achieve this by adding attribute to the model directly : link
Occasionally, when casting models to an array or JSON, you may wish to add attributes that do not have a corresponding column in your database. To do so, first define an accessor for the value:
public function getHasMessageAttribute()
{
return "the message that you want to pass";
}
After creating the accessor, add the attribute name to the appends property on the model. Note that attribute names are typically referenced in "snake case", even though the accessor is defined using "camel case":
{
/**
* The accessors to append to the model's array form.
*
* #var array
*/
protected $appends = ['has_message'];
}
There are many ways to go from here but;
and finally pass the message through CategoryResource
'message'=> $this->has_message,
if you want to separate that from the DB entries you can always use with.
I want to make custom validation function like built-in validation required. I have example code here:
Model:
use yii\base\Model;
class TestForm extends Model
{
public $age;
public function rules(){
return [
['age', 'my_validation']
];
}
public function my_validation(){
//some code here
}
}
View:
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
$this->title = 'test';
?>
<div style="margin-top: 30px;">
<?php $form = ActiveForm::begin(); ?>
<?= $form->field($model, 'age')->label("age") ?>
<div class="form-group">
<?= Html::submitButton('submit', ['class' => 'btn btn-primary']) ?>
</div>
<?php ActiveForm::end(); ?>
</div>
Controller:
use app\models\form\TestForm;
use yii\web\Controller;
class TestController extends Controller
{
public function actionIndex(){
$model = new TestForm();
if($model->load(\Yii::$app->request->post())){
return $this->render('test', array(
'model'=>$model,
'message'=>'success'
));
}
return $this->render('test', array('model'=>$model));
}
}
in this example I have a field for age and this my_validation function should check if age is over 18 before submit and throw error if age is under 18. This validation should be processed by ajax like it is in case of required rule if you try to submit empty field.
Although you can use Conditional Validators when and whenClient too in your scenario but I would recommend using a more sophisticated way which is to define a custom validator because according to the docs
To create a validator that supports client-side validation, you should
implement the yii\validators\Validator::clientValidateAttribute()
method which returns a piece of JavaScript code that performs the
validation on the client-side. Within the JavaScript code, you may use
the following predefined variables:
attribute: the name of the attribute being validated.
value: the value being validated.
messages: an array used to hold the validation error messages for the attribute.
deferred: an array which deferred objects can be pushed into (explained in the next subsection).
So what you need to do is create a validator and add it to your rules against the field you want.
You need to be careful copying the following code IF you haven't provided the actual model name and update the field names accordingly.
1) First thing to do is to update the ActiveForm widget to the following
$form = ActiveForm::begin([
'id' => 'my-form',
'enableClientValidation' => true,
'validateOnSubmit' => true,
]);
2) Change your model rules() function to the following
public function rules()
{
return [
[['age'], 'required'],
[['age'], \app\components\AgeValidator::className(), 'skipOnEmpty' => false, 'skipOnError' => false],
];
}
3) Remove the custom validation function my_validation() from your model i hope you are checking the age limit in it to be 18+ we will move that logic into the validator.
Now create a file AgeValidator.php inside components directory, if you are using the basic-app add the folder components inside the root directory of the project if it does not exist create a new one, and copy the following code inside.
BUT
I have assumed the name of the Model that is provided by you above so if it not the actual name you have to update the field name inside the javascript statements within clientValidateAttribute function you see below in the validator because the id attribute of the fields in ActiveForm is generated in a format like #modelname-fieldname (all small case) so according to above given model, it will be #testform-age do update it accordingly otherwise the validation wont work. And do update the namespace in the validator below and in the model rules() if you plan to save it somewhere else.
<?php
namespace app\components;
use yii\validators\Validator;
class AgeValidator extends Validator
{
public function init()
{
parent::init();
$this->message = 'You need to be above the required age 18+';
}
public function validateAttribute($model, $attribute)
{
if ($model->$attribute < 18) {
$model->addError($attribute, $this->message);
}
}
public function clientValidateAttribute($model, $attribute, $view)
{
$message = json_encode($this->message, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
return <<<JS
if (parseInt($("#testform-age").val())<18) {
messages.push($message);
}
JS;
}
}
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.
I went through offical guide and found a way to envelop JSON data like this.
use yii\rest\ActiveController;
class UserController extends ActiveController
{
public $modelClass = 'app\models\User';
public $serializer = [
'class' => 'yii\rest\Serializer',
'collectionEnvelope' => 'items',
];
}
This works perfect when I have a collection and then I have a response like this.
{
products:....
}
But what I want to do is that i have a envelope for single data. For example if I do products/10 GET request to get.
{
product:
}
Hope somebody figured it out.
Single Data Envelope is not supported by \yii\rest\Serializer. At least until Yii 2.0.6 only collections get enveloped in order to add _links and _meta data objects to the response.
To envelope single data resource objects you'll need to override ActiveController's default view action within your Controller :
public function actions()
{
$actions = parent::actions();
unset($actions['view']);
return $actions;
}
public function actionView($id)
{
$model = Product::findOne($id);
return ['product' => $model];
}
Old, but I just bumped into here with the same problem.
And found a better (I think) solution: create your own serializer class extending \yii\rest\Serializer:
class Serializer extends \yii\rest\Serializer
{
public $itemEnvelope;
public function serializeModel($model)
{
$data = parent::serializeModel($model);
if($this->itemEnvelope)return [$this->itemEnvelope=>$data];
return $data;
}
}
And then use it like this:
public $serializer = [
'class' => '[your-namespace]\Serializer',
'collectionEnvelope' => 'list',
'itemEnvelope' => 'item'
];