Managing views and api from the same controller - laravel

I have an issue with my app, that is "hybrid", what I mean by "hybrid" controllers have to manage both views ans APIs.
So, basically, for each controller, I must check:
if $request->wantsJson(){
... // Client rendering using Angular, return json
}else{
// Server rendering Using blade, return view
}
I don't like the fact to have a conditional in every controller method.
I also wouldn't like to have a API folder with a copy of all my controller, there would be a lot of duplicated code.
How should I do it?

I would suggest to create a separate class to handle output ex: class ResultOutput with the method, output.
So, in your controller, when you are ready to output your data, just create a new instance of ResultOutput class, and call method output with relevant data.
In ResultOutput class, inject Request object so you can determine the method of the output based on above logic.
Ex: In your controller:
return (new ResultOutput())->output($data);
In ResultOutput class:
class ResultOutput()
{
private $type;
public __construct(Request $request) {
$this->output = 'view';
if ($request->wantsJson()) {
$this->output = 'json';
}
}
public method output($data) {
if ($this->type =='view') {
// return the view with data
} else {
// return the json output
}
}
}
In this way, if you need to introduce new output method (ex: xml), you can do it without changing all your controllers.

Related

How to call Service class function?

use App\Http\Controllers\ATransaction;
use App\Service\ATransactionServices;
class TransactionController extends Controller {
public function ATransaction(Request $request, ATransactionServices $ATransaction){
try {
$validated_request = $this->validateRequest($request);
} catch (ValidationException $exception) {
return redirect()->back()->withInput()->withErrors($exception->errors());
}
How to call my service class function to replace into
$validated_request = $this->validateRequest($request);
Can someone show me the right way?
You can traits concepts or can directly use DI ( dependency Injection ) to implement service into this class.
For traits, you can check How to create traits in Laravel
And if you want to use Dependency Injection then do something like:
public function __construct(ATransactionServices $aTransactionServices)
{
$this->aTransactionServices = $aTransactionServices;
}
Then inside the method you can directly use the service methods:
$this->aTransactionServices->methodName();
If you want to use method inside the try and catch than simplest way is to add them in controller.
class TransactionController extends Controller {
//This will help in directly injecting the service into the container
public function __construct(ATransactionServices $aTransactionServices)
{
$this->aTransactionServices = $aTransactionServices;
}
//Otherwise you can create a custom request to handle validations on the request level
public function ATransaction(Request $request){
try {
$validated_request = $this->aTransactionServices->validateRequest($request);
//Here you can setup the validation mechanism like custom status based validation e.g. $validated_request['status'] which can be true or false otherwise use validator instance to use $validated_request->fails() to check
if($validated_request->fails()) {
//Here pass the data and you can handle on catch how you want to send response
throw new Exception('Exception details or validator instance');
}
} catch (Exception $exception) {
return redirect()->back()->withInput()->withErrors($exception->errors());
}
}
But if your only purpose is to handle the validations than you can use custom request in laravel for validations handling: Custom Request
Hope this works for you and resolves your issue.
have a great day.
$validated_request = $ATransaction->validateRequest($request);

Laravel Controller member variable

Is there any possibility in laravel to keep the state of the controller object?
In every example I found, the controller looks the following way:
class MyController extends Controller {
public function getView(){ //return the view }
public function postData() { //save the data }
}
What I would do is to call a service which loads specific data from my data base and return it to the view. In the example above this should be done within the getView() function. What if I need the same data in my postData() function.. Then I have to make another database call in the postData function. It is not possible to have a member variable in 'MyController' and to load the data only once for the class because of routing in laravel. When I call via routing the getView function I get another instance of MyController than I get if I call postData. Is there a possibility to call a specific function only once for the whole controller and to get access to this values from all the functions within the controller?
Is there a possibility to call a specific function only once for the
whole controller and to get access to this values from all the
functions within the controller?
As per my understanding it it not possible. Actually any function of controller is being called via routes. When your any route has been called every time the new object of controller is being created. But it has other way of round. You can use Cache. You can implement it as below:
Call to your specific function of controller.
Get the data from the database.
Store it in Cache for other functions.
In other functions check is data available in Cache? then get from Cache else call your database function to get the data.
Simply in coding as below:
Use Cache;
class MyController extends Controller {
public function getView(){
$data = call_to_database_and_returned_data();
Cache::put('data',$data,120);
return the view
}
public function postData() {
$data = null;
if(Cache::has('data')) {
$data = Cache::get('data');
} else {
$data = call_to_database_and_returned_data();
Cache::put('data',$data,120);
}
}
}
Syntax Description:
Cache::put('name_to_save_data',$your_variable_to_save,for_number_minutes);

How to trigger a method in all pages request in Yii?

In the header section of my website I want to show new message. I have a method that fetches new methods and return them. The problem is that header section is in thelayout section and I don't want to repeat one method in all of my controllers.
How to achieve this by not copying the method to all of my controllers? I want to trigger newMessages() method on every page request to gather new messages for logged in user. How to do this the right way?
In your controller overwrite the oOntroller class function beforeAction()
protected function beforeAction($event)
{
$someResult = doSomething()
if ($someResult == $someValue)
{
return true;
}
else
{
return true;
}
}
The return value can be used to stop the request dead in its tracks. So if it returns false, the controller action is not called, and vice versa().
References : http://www.yiiframework.com/doc/api/1.1/CController#beforeAction-detail
You can use import controller in another controller action. something like below
class AnotherController extends Controller
{
public function actionIndex()
{
Yii::import('application.controllers.admin.YourController'); // YourController is another controller in admin controller folder
echo YourController::test(); // test is action in YourController
}
}

Storing data in Zend_Registry vs class properties

I'm building a Zend Framework application wherein the Model layer is separated into Services and Models. Controller actions call service methods, which in turn call model methods.
For instance: LanguagesController::addAction() checks if a form is submitted and valid. If so, it passes the form data to Service_Language::add(), where some business logic is applied to the data before calling Model_Language::add(), which effectively adds the record to the database.
This means that most controller actions will need an instance of a service class, and most methods in the service class will need an instance of a model class.
I used to do it like this (example of a Service class)
class Service_Language
{
public function add()
{
$languageModel = new Model_Language;
// perform some business logic and add record
}
public function edit()
{
$languageModel = new Model_Language;
// perform some business logic and edit record
}
public function delete()
{
$languageModel = new Model_Language;
// perform some business logic and delete record
}
}
Not only does it become cumbersome, in more complex applications where your controller actions call multiple Service methods, there's going to be multiple instances of the same Model class, which is just unnecessary.
A colleague told me to look into two options:
keep a Model instance in a property of the Service
keep a Model instance in Zend_Registry
I think the best solution would be the first option. The reason being that Zend_Registry acts as a global container. We don't want our Model instances to be available in our Controller actions, it's bad architecture. What are your opinions on this?
The first option could be implemented as follows:
class Service_Language
{
protected $_model = null;
function setModel()
{
$this->_model = new Model_Language();
}
function getModel()
{
if($this->_model == null)
{
$this->setModel();
}
return $this->_model;
}
public function add()
{
$languageModel = $this->getModel();
// perform some business logic and add
}
}
From your explanation it sounds like your services classes require tightly coupled models.
In which case I don't think public a public getter/setter for your model is necessary - would there ever realistically be a situation where you would need to set another model for the service?
In which case, assigning the model to a property makes sense - why not do this in the constructor?
class Service_Language
{
protected $_model = null;
public function __construct()
{
$this->_model = new Model_Language();
}
public function add()
{
// perform some business logic and add
$this->_model->add($data);
}
public function edit()
{
// perform some business logic and add
$this->_model->edit($data);
}
}
The constructor would have been a good option, but not every method in the service layer needs to have a model instance to do its job, so I ended up doing it like this. I'm relatively new to OOP programming, so I'm wondering if this is a good solution. Any thoughts are more than welcome.
class Service_Language
{
protected $_model = null;
protected function setModel()
{
$this->_model = new Model_Language();
}
protected function getModel()
{
if($this->_model == null)
{
$this->setModel();
}
return $this->_model;
}
// Function where model is needed
public function add($data)
{
// Perform some business logic
$this->getModel()->add($data);
// return something
}
// Function where no model is needed
public function add($data)
{
// Perform some business logic
// return something
}
}

Show validation messages on GET

We have a possibility that data loaded from a GET operation could be invalid for posting, and would like to be able to display the validation messages when the data is first loaded. The validation all takes place on server side using ValidationAttributes.
How can I force the validation summary to be displayed when the data is first loaded? I am guessing that I need to force errors into ModelState somehow, but I first need to get them out of the model class.
I ended up adding a validation method for the model class which adds errors to the ModelState. Then I created and added a custom ModelValidator and AssociatedValidatorProvider
for calling it during the normal validation that takes place during form binding. That way the controller actions that don't bind to the Model class directly can still have a call to the model's .Validate(ModelState) method to fake a validation. This approach works well for server-side-only validation.
UserInfo Model class:
private IEnumerable<RuleViolation> GetRuleViolations()
{
List<RuleViolation> violationList = new List<RuleViolation>();
if (String.IsNullOrWhiteSpace(FirstName))
violationList.Add(new RuleViolation("First Name is required.", FirstName"));
return violationList;
}
public void Validate(System.Web.Mvc.ModelStateDictionary ModelState)
{
foreach (RuleViolation violation in GetRuleViolations())
{
ModelState.AddModelError(violation.PropertyName, violation.ErrorMessage);
}
}
This is how it can be used directly from a controller action. In this action the Model class object is returned as part of the UserSearch model.
public ActionResult Search(UserSearch model)
{
if (this.ModelState.IsValid)
{
model.Search();
if (model.UserInfo != null )
{
model.UserInfo.Validate(ModelState);
}
}...
That is all I had to do for the particular use case I was working on. But I went ahead and completed the work to do "normal" validation on a postback: created a simple ModelValidator, with the Validate override looking like this. If you followed the above pattern in all of your Model classes you could probably reusue this for them, too.
public override IEnumerable<ModelValidationResult> Validate(object container)
{
var results = new List<ModelValidationResult>();
if (Metadata.Model != null)
{
UserInfoViewModel uinfo = Metadata.Model as UserInfoViewModel;
foreach (var violation in uinfo.GetRuleViolations())
{
results.Add(new ModelValidationResult
{
MemberName = violation.PropertyName,
Message = violation.ErrorMessage
});
}
}
return results;
}
Finally, extend AssociatedValidationProvider to return this ModelValidator and add it to the ModelValidationProviders collection in Application_Start. There is a writeup of this at http://dotnetslackers.com/articles/aspnet/Customizing-ASP-NET-MVC-2-Metadata-and-Validation.aspx#s2-validation
I don't know if understand what you need, but here is it...
run validation to display the validation summary when the form is loaded, using jquery
$(document).ready(function() {
$('#FormId').valid();
});

Resources