I created custom actions for rest api in yii2
my codes are:
namespace app\controllers;
use yii\rest\ActiveController;
use yii\web\Response;
use Yii;
class RsController extends ActiveController{
public $modelClass='app\models\Mymodel';
/*some another actions*/
public function actionOne($id){
return \app\models\Anothermodel::findAll(['my_id'=>$id]);
}
public function actionTwo($id){
return \app\models\Anothermodel::findAll(['my_name'=>'xxxx']);
}
}
I know we can override fields function in model to get special fields but
now I wanted to get different fields for actionOne and actionTwo (of a model)
How can I override fields function in Anothermodel for this purpose?
I found my answer from here
I create a component like this
<?php
namespace app\components;
class Serializer extends \yii\rest\Serializer {
public $defaultFields;
public $defaultExpand;
public function init() {
parent::init();
$this->defaultFields = !is_null($this->defaultFields) ? implode(",", $this->defaultFields) : $this->defaultFields;
$this->defaultExpand = !is_null($this->defaultExpand) ? implode(",", $this->defaultExpand) : $this->defaultExpand;
}
protected function getRequestedFields() {
$fields = is_null($this->request->get($this->fieldsParam)) ? $this->defaultFields : $this->request->get($this->fieldsParam);
$expand = is_null($this->request->get($this->expandParam)) ? $this->defaultExpand : $this->request->get($this->expandParam);
return [
preg_split('/\s*,\s*/', $fields, -1, PREG_SPLIT_NO_EMPTY),
preg_split('/\s*,\s*/', $expand, -1, PREG_SPLIT_NO_EMPTY),
];
}
}
and then in my controllers action set my fields
like this.
public function actionOne($id){
$this->serializer['defaultFields'] = ["field1",
"field2"];
return new \yii\data\ActiveDataProvider([
'query' => \app\models\Anothermodel::find()->where(['my_id'=>$id]),
]);
}
public function actionTwo($id){
$this->serializer['defaultFields'] = ["field1",
"field2","field3"];
return new \yii\data\ActiveDataProvider([
'query' => \app\models\Anothermodel::find()->where(['my_id'=>$id]),
]);
}
I suggest to use events
public function actionPublic()
{
\yii\base\Event::on(Thing::class, Thing::EVENT_AFTER_FIND, function ($event) {
$event->sender->scenario = Thing::SCENARIO_SEARCH_PUBLIC;
});
return new ActiveDataProvider([
'query' => Thing::find(),
]);
}
public function actionPrivate()
{
\yii\base\Event::on(Thing::class, Thing::EVENT_AFTER_FIND, function ($event) {
$event->sender->scenario = Thing::SCENARIO_SEARCH_PRIVATE;
});
return new ActiveDataProvider([
'query' => Thing::find(),
]);
}
and inside of ActiveRecord (Thing in my case) check the scenario in fields() method
public function fields()
{
$fields = parent::fields();
if ($this->scenario === self::SCENARIO_SEARCH_PUBLIC) {
unset($fields['field1'], $fields['field2'], $fields['field3'], $fields['field4']);
}
return $fields;
}
check my answer in gihub
Related
I'm building livewire components that shares 50% of public properties and almost 90% of submit function logic.
each component using this trait has its own rules according to its html-form fields. and also each component perform some custom logic after validating the data. other that that they are all the same.
<?php
namespace App\Traits;
trait ParentServiceComponent
{
public $desc = '';
public function rules()
{
return [
'desc' => 'required|max:2000'
];
}
public abstract function componentCustomLogic(array $data);
public function submit()
{
$data = $this->validate();
$performCusomLogic = $this->componentCustomLogic($data);
// save to db and show success message
}
}
here an example of two components that uses this trait.
<?php
namespace App\Http\Livewire;
use Livewire\Component;
use App\Traits\ParentServiceComponent;
class RequestService extends Component
{
public $type = '';
use ParentServiceComponent { rules as traitRules; }
public function rules()
{
return array_merge($this->traitRules, [
'type' => 'required|max:200'
]);
}
public function componentCustomLogic(array $data)
{
// do the logic of this component here
}
public function render()
{
return view('livewire.request-service');
}
}
<?php
namespace App\Http\Livewire;
use Livewire\Component;
use App\Traits\ParentServiceComponent;
class ReplyService extends Component
{
public $body = '';
use ParentServiceComponent { rules as traitRules; }
public function rules()
{
return array_merge($this->traitRules, [
'body' => 'required|max:200'
]);
}
public function componentCustomLogic(array $data)
{
// do the logic of this component here
}
public function render()
{
return view('livewire.reply-service');
}
}
so my question is: am I doing it right?
I'm trying to make an api that have lists and inside each list there is anther list inside of it called cards and the cards list is the cards of this list.
I tried to show it in index function and didn't work it was like this:
public function index()
{
// $list = List -> cards();
$list = List::cards();
return response( $list );
}
Card Model:
public function list()
{
return $this->belongsTo( List::class() );
}
Card Model:
public function cards()
{
return $this->hasMany( Card::class() );
}
What i want to output is json data like this:
"lists":[
'name':listname
'cards':[
'card one': card name,
]
]
If you use Laravel framework use Resource for response, in Resource of laravel you can load cards. For example in ListController :
public function index()
{
return ListResource::collection(List::all()->paginate());
}
And in ListResource :
public function toArray($request)
{
'cards' => CardResource::collection('cards');
}
belongsTo or hasMany accepts model name as a first argument. In your case you need to pass your model class name in your relations methods.
public function list()
{
return $this->belongsTo(List::class);
}
and
public function cards()
{
return $this->hasMany(Card::class);
}
So if you want to receive models including relations you can use with method.
return response(List::query()->with('cards'));
You can use resources.
Http\Resources\List:
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class List extends JsonResource
{
public function toArray($request)
{
$cards = [];
foreach ($this->cards as $card) {
$cards[] = $card->name;
}
return [
'name' => $this->name,
'cards' => $cards,
];
}
}
Http\Controllers\ListController:
namespacce App\Http\Controllers;
use App\Http\Resources\List as ListResource;
use App\Components\List;
class ListController extends Controller
{
$lists = List::query()->get();
return ListResource::collection($lists)->response();
}
I am using a repository pattern in my Laravel 4 project but come across something which I think I am doing incorrectly.
I am doing user validation, before saving a new user.
I have one method in my controller for this:
public function addNewUser() {
$validation = $this->userCreator->validateUser($input);
if ( $validation['success'] === false )
{
return Redirect::back()
->withErrors($validation['errors'])
->withInput($input);
}
return $this->userCreator->saveUser($input);
}
Then the validateUser method is:
public function validate($input) {
$rules = array(
'first_name' => 'required',
'last_name' => 'required',
'email_address' => 'unique:users'
);
$messages = [
];
$validation = Validator::make($input, $rules, $messages);
if ($validation->fails())
{
$failed = $validation->messages();
$response = ['success' => false, 'errors' => $failed];
return $response;
}
$response = ['success' => true];
return $response;
}
This may be okay, but I dont like doing the if statement in my controller? I would rather be able to handle that in my validation class.
But to be able to redirect from the validation class, I need to return the method in the controller.
What if I then want to have 5 methods called, I cant return them all?
I would like to be able to simply call the methods in order, then in their respective class handle what I need to and if there is any errors redirect or deal with them. But if everything is okay, simply ignore it and move to the next function.
So example:
public function addNewUser()
{
$this->userCreator->validateUser($input);
$this->userCreator->formatInput($input);
$this->userCreator->sendEmails($input);
return $this->userCreator->saveUser($input);
}
If doing the if statement in the controller isn't as bad as I think then I can continue, but this seems incorrect?
For repository pattern, you can use this :-
setup your basemodel like this
<?php namespace App;
use Illuminate\Database\Eloquent\Model;
class BaseModel extends Model{
protected static $rules=null;
protected $errors=null;
public function validateForCreation($data)
{
$validation=\Validator::make($data,static::$rules);
if($validation->fails())
{
$this->errors=$validation->messages();
return false;
}
return true;
}
/**
* #return errors
*/
public function getErrors() { return $this->errors; }
}
now in your repository, add these methods
protected $model;
protected $errors=null;
public function model(){ return $this->model; }
public function getErrors(){ return $this->errors; }
public function create($inputs)
{
if(!$this->model->validateForCreation($inputs))
{
$this->errors=$this->model->getErrors();
return false;
}
$new=$this->model->create($inputs);
return $new;
}
and the controller will look like this..
public function postCreate(Request $request)
{
$inputs=$request->all();
if($new=$this->repo->create($inputs))
{
return redirect()->back()
->with('flash_message','Created Successfully');
}
return redirect()->back()->withInput()->withErrors($this->repo->getErrors())
->with('flash_message','Whoops! there is some problem with your input.');
}
I have the following model:
class Person
{
public $name;
function __Construct( $name )
{
$this->name = $name;
}
}
I have the following controller:
class NavigationController extends Controller
{
public function indexAction()
{
$people = array(
new Person("James"),
new Person("Bob")
);
return $this->render('FrameworkBundle:Navigation:index.html.php', $people);
}
}
How do I get access to the model array in the view. Is there a way to access the model directly or do I have to assign a property like so:?
class NavigationController extends Controller
{
public function indexAction()
{
$people = array(
new Person("James"),
new Person("Bob")
);
return $this->render('FrameworkBundle:Navigation:index.html.php', array( "model" => $people ) );
}
}
View:
<?php
foreach( $model as $person )
{
echo $person->title;
}
?>
The problem with the above will be that it can be changed by a user to
return $this->render( 'FrameworkBundle:Navigation:index.html.php', array( "marybloomingpoppin" => $people ) );
With the example view you used, you already had the correct implementation:
class NavigationController extends Controller
{
public function indexAction()
{
$people = array(
new Person("James"),
new Person("Bob")
);
return $this->render('FrameworkBundle:Navigation:index.html.php', array( "model" => $people ) );
}
}
You mentioned the concern that somebody could change the assignment in the controller, but this is something you always have if somebody changes the name of a variable only in one place and not in all. So I don't think this is an issue.
i got such form
class CC extends CFormModel
{
public $static_field;
public $fields;
public function rules()
{
return array(
array('static_field, testF', 'required')
);
}
public function getForm()
{
return new CForm(array(
'showErrorSummary'=>true,
'elements'=>array(
'static_field'=>array(),
'testF'=>array(),
),
'buttons'=>array(
'submit'=>array(
'type'=>'submit',
'label'=>'Next'
)
)
), $this);
}
public function attributeLabels()
{
return array(
'static_field' => 'static_field'
);
}
public function __get($name)
{
if (isset($this->fields[$name]))
return $this->fields[$name];
else
return '';
}
public function __set($name, $value)
{
$this->fields[$name] = $value;
}
}
i want to add dynamical field testF
i try to use __get\__set and array for values, but nothing work. any ideas?
If by dynamic you mean not required, you can add it as a property just as you have done with static_field. All attributes, or fields, are encapsulated member data of your FormModel class. So, if you wanted to add your dynamic_field attribute, you could add it in this manner:
class CC extends CFormModel
{
public $static_field;
public $dynamic_field;
public function rules()
{
return array(
array('static_field','required'),
array('dynamic_field','safe'),
);
}
}
Also, you're not exactly following the dominant usage pattern for this type of class. If I were you, I would suggest creating some CRUD through gii and examining the usage patterns for models and forms.