I'm trying to send a specific message from Model in beforeSave() method. Flash messages don't work. I could send this message from Controller and use some parameters but I don't this this best solution. Use of print isn't good either.
So my question is how to send any message to controller/view from model?
You have to bubble an error message, try this
in your model :
public function beforeSave($options = array()){
if($not_ok){
$this->error = __('My error message');
return false;
}
return true;
}
in your controller :
public function add(){
$save = $this->Modelname->save($this->request->data);
if(!$save){
$this->Session->setFlash($this->Modelname->error);
$this->redirect($this->referer());
}
}
Well Session->setFlash() will not work, obviously, as it's part of a Session component, but
Session component uses static singleton class CakeSession, which has method CakeSession::write() all you've to do is pass array to write method that would have same structure as Session::setFlash() would generate and therefore when you use Session::flash() in view you will get same result as from setFlash() from controller.
For refrence: http://api.cakephp.org/2.2/class-CakeSession.html
Snippet from comment, to be placed in Model method.
App::uses('CakeSession','Model/Datasource');
CakeSession::write('Message', array(
'flash' => array(
'message' => 'your message here',
'element' => 'default',
'params' => null,
),
));
By doing the following you will be able to set flashes within your models at any point and not have to worry about declaring them again within your controller because they will get automatically set within your app controller before the page is rendered.
In AppController:
public function beforeRender() {
parent::beforeRender();
$this->generateFlashes();
}
public function generateFlashes() {
$flashTypes = array('alert', 'error', 'info', 'success', 'warning');
$model = $this->modelClass;
foreach($flashTypes as $type) {
if(!empty($this->$model->$type)) {
$message = '<strong>' . ucfirst($type) . ':</strong> ' . $this->$model->$type;
$this->Flash->error($message, array('escape' => false));
}
}
}
In Model:
public function beforeSave($options = array()){
if($not_ok){
$this->error = __('My error message');
return false;
}
return true;
}
In Controller:
public function add(){
// $this->modelClass will use whatever the actual model class is for this
// controller without having to type it out or replace the word modelClass
$save = $this->{$this->modelClass}->save($this->request->data);
if(!save){
// no need to set flash because it will get created in AppController beforeRender()
$this->redirect($this->referer());
}
}
Related
PHP Version:7.2
Laravel Version:6.2
I am doing a simple project by laravel by article.
When I meet with redirect()->action, I am a little confused about that.
I want to pass a variable named id by redirect()->action but it does't work.
Error Message is Missing required parameters for [Route: blog/post.show] [URI: blog/post/{post}].
If I remove the variable name, only pass the variable value and it would work. I read the manual but still could not understand the reason. Could you help me explain the logic. Thank you. Below is the sample code.
Router.php
Route::group(['prefix' => 'blog',
'as' => 'blog/',
'namespace' => 'Blog'],
function(){
Route::resource('/post',"PostController");
});
PostController.php
Create new blog post ( wrong )
Couldn't understant why this does't work ? Variable name($id) is the same.
public function store(Request $request)
{
$post = new BlogPost;
$post->title = $title;
$post->content = $content;
$post->save();
return redirect()->action(
'Blog\PostController#show', ['id' => $post->id]
);
}
Create new blog post ( correct )
public function store(Request $request)
{
$post = new BlogPost;
$post->title = $title;
$post->content = $content;
$post->save();
return redirect()->action(
'Blog\PostController#show', [$post->id]
);
//Or 'Blog\PostController#show', $post->id
}
Show the new blog post
public function show($id)
{
$post = BlogPost::find($id);
if(! $post) {
abort(404);
}
$content = $post->content;
return view("blog.post", [
"title" => $post->title,
"content" => $content,
]);
}
Thank you
Here is code :
return redirect()->route('routename', ['id' => 1]);
You got the error message because you are using the Resource Route and it will automatic bind the Model with Route
For More Info please refer: https://laravel.com/docs/6.x/routing#route-model-binding
I encountered this error myself when trying to use a redirect()->action. Here's a simple example that will fail in just the same way.
class SimpleController extends Controller {
public function index() {
return redirect()->action([static::class, 'show'], ['id' => 7]);
}
public function show($id) {
// ... code goes here ...
}
}
And in the routes somewhere:
Route::resource('somethingsimpler', SimpleController);
The reason this fails is because default stub used by Route::resource for show is the same as the resource name. Have a read here: https://laravel.com/docs/9.x/controllers#actions-handled-by-resource-controller
Solution 1
We could change our original example to using 'somethingsimpler' instead of 'id'.
class SimpleController extends Controller {
public function index() {
return redirect()->action([static::class, 'show'], ['somethingsimpler' => 7]);
}
public function show($id) {
// ... code goes here ...
}
}
And in the routes somewhere:
Route::resource('somethingsimpler', SimpleController);
However, this seems to negate the whole purpose of using redirect()->action.
Solution 2
Reading further in the same document linked above, it seems you can set the resource name https://laravel.com/docs/9.x/controllers#restful-naming-resource-route-parameters.
class SimpleController extends Controller {
public function index() {
return redirect()->action([static::class, 'show'], ['id' => 7]);
}
public function show($id) {
// ... code goes here ...
}
}
And in the routes somewhere:
Route::resource('somethingsimpler', SimpleController)->parameters([
'somethingsimpler' => 'id'
]);
Solution 3 - Recommended
Reading the rest of the document, it becomes obvious that you can probably get away with not even naming the parameter.
class SimpleController extends Controller {
public function index() {
return redirect()->action([static::class, 'show'], [7]);
}
public function show($id) {
// ... code goes here ...
}
}
And in the routes somewhere:
Route::resource('somethingsimpler', SimpleController);
I am building a test API. I have created a Controller Page which extends from yii\rest\Controller. Actions needs to send a response.
To access actions in this controller, a service_id value needs to be posted. If present I need to evaluate if that service_id exists, if it is active and belongs to the user logged in. If validation fails, I need to send a response.
I am trying to do it using beforeAction(), but the problem is that return data is used to validate if action should continue or not.
So my temporary solution is saving service object in a Class attribute to evaluate it in the action and return response.
class PageController extends Controller
{
public $service;
public function beforeAction($action)
{
parent::beforeAction($action);
if (Yii::$app->request->isPost) {
$data = Yii::$app->request->post();
$userAccess = new UserAccess();
$userAccess->load($data);
$service = $userAccess->getService();
$this->service = $service;
}
return true;
}
public function actionConnect()
{
$response = null;
if (empty($this->service)) {
$response['code'] = 'ERROR';
$response['message'] = 'Service does not exist';
return $response;
}
}
}
But I can potentially have 20 actions which require this validation, is there a way to return the response from the beforeAction method to avoid repeating code?
You can setup response in beforeAction() and return false to avoid action call:
public function beforeAction($action) {
if (Yii::$app->request->isPost) {
$userAccess = new UserAccess();
$userAccess->load(Yii::$app->request->post());
$this->service = $userAccess->getService();
if (empty($this->service)) {
$this->asJson([
'code' => 'ERROR',
'message' => 'Service does not exist',
]);
return false;
}
}
return parent::beforeAction($action);
}
maybe paste in beforeAction after $this->service = $service;
if (empty($this->service)) {
echo json_encode(['code' => 'ERROR', 'message' => 'Service does not exist']);
exit;
}
I have some custom routes in my system:
---
Name: mysiteroutes
---
Director:
rules:
'create//$Action': 'CreateController'
Which has a custom controller to create a form:
class CreateController extends Page_Controller{
private static $allowed_actions = array(
'submit'
);
public function link($action = null) {
return $this->join_links('create/', $action);
}
public function index() {
$form = Form::create(
$this,
'',
FieldList::create(
TextField::create('Name', 'Name'),
$upload = new UploadField('Upload', 'Upload')
),
FieldList::create(
FormAction::create('submit', 'Submit')->setAttribute('class', 'btn btn-success')
),
RequiredFields::create('Name')
);
if($this->request->isPost()) return $form;
return $this->customise(array('Form'=>$form))->renderWith(array("Create", "Page"));
}
public function submit($data, $form = null) {
$params = $this->getRequest()->params();
var_dump($params);
}
}
When I try and upload something it calls Field() on my controller and then fails as it's not there. I can add it and it calls it correctly however I have no idea what to put in it. I've looked through the Field() function in UploadField.php however there is a lot of code there which I probably shouldn't just copy.
How should I manage the upload of the file in my custom controller or can I forward it to the core framework somehow?
UploadField expects to have a route based on the Form name, in your case ''.
If you would change the name of the form to form it would call form/field/Upload/upload. What this does is get the form, then get the field with the name Upload and call the method upload on that class.
Unfortunately, the way you are using the form (which I showed you in an earlier answer :( ) does not support this.
We could solve it like this;
CreateController
class CreateController extends Page_Controller
{
private static $allowed_actions = [
'form'
];
public function link($action = null)
{
return $this->join_links('create', $action);
}
public function index()
{
return $this->renderWith(array("Create", "Page"));
}
public function form()
{
return UploadForm::create($this, 'form', 'submit');
}
public function submit($data, $form = null)
{
$params = $this->getRequest()->params();
var_dump($params);
}
}
Form
// create an extra class for the form to keep your controller clean
class UploadForm extends Form
{
public function __construct($controller, $name, $action)
{
$fields = FieldList::create(
TextField::create('Name', 'Name'),
UploadField::create('Upload', 'Upload')
);
$actions = FieldList::create(
FormAction::create($action, 'Submit')
->setAttribute('class', 'btn btn-success')
);
$validator = RequiredFields::create('Name');
parent::__construct($controller, $name, $fields, $actions, $validator);
}
}
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 piece of code:
if(Request::ajax())
{
$response_values = array(
'validation_failed' => 1,
'errors' => $validator->errors->toArray()
);
return Response::json($response_values);
}
else
{
return Redirect::route("resource.create")
->withInput()
->withErrors($validator->errors);
}
I have this a lot in my code, and would like to find a way to automate this.
I tried creating a method in BaseController but it doesn't redirect properly, I also tried an after filter, but I was unable to pass parameters to this after filter, as I would need to pass errors and route.
How could I achieve this?
This is not working for you?
class BaseController extends \Controller {
public function processAndRedirectError($validator)
{
if(Request::ajax())
{
$response_values = array(
'validation_failed' => 1,
'errors' => $validator->errors->toArray()
);
return Response::json($response_values);
}
else
{
return Redirect::route("resource.create")
->withInput()
->withErrors($validator->errors);
}
}
}
class MyController extends BaseController {
public function store()
{
$validator = Validator::make(...);
return $this->processAndRedirectError($validator);
}
}