I've just learned of model observers and would like to move some of my logic from controller to observer. Here's what I have:
AppServiceProvider.php
public function boot()
{
WorkOrder::observe(WorkOrderObserver::class);
}
WorkOrderObserver.php
namespace App\Observers;
use App\Site;
use App\WorkOrder;
use Carbon\Carbon;
use App\WorkOrderNumber;
class WorkOrderObserver
{
public function creating(WorkOrder $workOrder)
{
$branchOfficeId = Site::findOrFail($request->site_id)->branch_office_id;
$today = Carbon::today('America/Los_Angeles');
$todaysWorkOrderCount = WorkOrder::where('created_at_pst', '>=', $today)->count();
$workOrder->work_order_number = (new WorkOrderNumber)
->createWorkOrderNumber($branchOfficeId, $todaysWorkOrderCount);
$workOrder->completed_by = null;
$workOrder->status_id = 1;
$workOrder->work_order_billing_status_id = 1;
$workOrder->created_at_pst = Carbon::now()->timezone('America/Los_Angeles')
->toDateTimeString();
}
}
Problem is accessing the request from within the observer. I don't see anything in the docs. I found one thread here that refers to this and it suggested using the request helper function. I tried request('site_id') but it was empty.
This is so simple I'm a bit embarrassed I posted it. Anyway, in case someone finds this thread, here's the solution. In your observer, add a constructor that accepts the request and sets a property.
protected $request;
public function __construct(Request $request)
{
$this->request = $request;
}
You can request object using app helper function of Laravel.
protected $request;
public function __construct(WorkOrderNumber $workorder)
{
$this->request = app('request');
}
get request data in observer laravel
request() helper should work:
if (request()->has('password')) {
$user->password = bcrypt(request()->password);
}
ref: https://www.codegrepper.com/code-examples/php/get+request+data+in+observer+laravel
Related
I created a route & contronller:
Route::group(['prefix' => 'test'], function () {
Route::get('product/{id}', ['uses' => 'ProductController#getProduct']);
});
ProductController:
class ProductController extends MyController {
public $_params = null;
public function __construct(Request $request) {
$this->request = $request;
$this->_params = $request->all();
$options = array();
parent::__construct($options);
}
public function getProduct() {
dd($this->_params);
}
}
I requested: http://localhost/test/product/123
But the id = 123 not exist in $this->_params
Request params are input data like POST data. To access route params you will need to make another class property like "$routeParams"
class ProductController extends MyController {
public $_params = null;
public $routeParams = null;
public function __construct(Request $request) {
$this->request = $request;
$this->_params = $request->all();
$this->routeParams = $request->route()->parameters();
$options = array();
parent::__construct($options);
}
public function getProduct() {
dd($this->routeParams);
}
}
I understand need to implement your logic on top of the Laravel, but I would suggest that you do that in some Services, Actions, Domain.... Maybe this can help: https://laravel-news.com/controller-refactor
You can try do it like basic controller from documentation
and make some custom service for complex stuff.
class ProductController extends Controller
{
public function getProduct($id)
{
$productService = new ProductService($id);
//....
}
}
If you want to get an array of the route parameters, you need to use $request->route()->parameters(), not $request->all()
$request->all() returns the query parameters for GET requests
$request->all() only get parameter from header, body and URL but can't get parameter from route product/{id}
You should replace func getProduct to param id
public function getProduct($id) { dd($id); }
İf you want all params in request use $request->all() method but you want only id in url $request->id
I am new to Laravel. I have some functions in PaymentController. I want to call those functions from SmartpaySController. Here is the function which is available in PaymentController. Help me to call that function by staying in SmartpaySController.
public function getPaymentFailed($paymentId) {
$transactionData = $this->paymentRepo->find($paymentId);
if($transactionData) {
$data['quote'] = $this->quoteRepo->getQuoteById($transactionData->quote_id);
$data['metaTitle'] = 'Payment failed';
$data['returnMessage'] = $transactionData->return_message;
return view('payment::payment.quote_payment_failed', $data);
}
}
Thank you.
Instead of calling controller methods, the better practice is that you can create traits like: app/Traits and extend in controller
//trait
trait traitName {
public function getData() {
// .....
}
}
//Controller
class ControlelrName extends Controller {
use TraitName;
}
I recomend you to not call functions from one controller to another.
Make Helpers, Resources or implement same feature in other way
Never use controllers as object
But if you want to do it anyway you can use:
SomeController.php
class SomeController extend Controller {
public function someFunction(Request $request) {
// Here Some Code
}
}
YourController.php
use SomeController;
...
public function getPaymentFailed(Request $request, $paymentId) {
$controller_data = (new SomeController)->someFunction($request);
$transactionData = $this->paymentRepo->find($paymentId);
if($transactionData) {
$data['quote'] = $this->quoteRepo->getQuoteById($transactionData->quote_id);
$data['metaTitle'] = 'Payment failed';
$data['returnMessage'] = $transactionData->return_message;
return view('payment::payment.quote_payment_failed', $data);
}
}
Change:
public function getPaymentFailed($paymentId)
to:
public static function getPaymentFailed($paymentId)
This will make it staticly available in your SmartpaySController by doing:
PaymentController::getPaymentFailed($paymentId);
You can make use of Real-Time Facades
Using real-time facades, you may treat any class in your application
as if it were a facade.
To generate a real-time facade, prefix the namespace of the imported
class with Facades:
//...
use use Facades\App\Http\Controllers\SomeController;
//...
return SomeController::getPaymentFailed($request, $paymentId);
I have this code in my CompanyController
use App\Services\CompanyService;
public function store(CompanyService $companyService) {
$result = $companyService->store();
return response()->json($result);
}
And this code in my CompanyService
use stdClass;
use App\Company;
use Illuminate\Http\Request;
public function store(Request $request, Company $company) {
// this also not work
dd($request->all());
$data = new stdClass;
$data->status = 1;
$data->message = 'success';
return $data;
}
When i run this code, Laravel show error
Too few arguments to function App\Services\CompanyService::store() 0
passed but exactly 1 expected
I know that type hint dependecy injection issue, because it work in Controllers but not work in my CompanyService when i call store() without params
How can i fix this and make it work in my CompanyService ?
Edit:
You can use call() method of service container
In your case:
public function store()
{
$companyService = app(CompanyService::class);
$result = $companyService->call('store');
return response()->json($result);
}
Unfortunately it's not possible to method inject dependencies but you can inject your dependencies in your construct method, so in your case it would be like this:
use stdClass;
use App\Company;
use Illuminate\Http\Request;
class Foo
{
protected $request;
protected $company;
public function __construct(Request $request, Company $company)
{
$this->request = $request;
$this->company = $company;
}
public function store()
{
// this works
dd($this->request->all());
// Also injected
dd($this->company);
$data = new stdClass;
$data->status = 1;
$data->message = 'success';
return $data;
}
}
PS:
I believe laravel handles method injection in one of it's middlewares so as far as I know there is not possible way to perform method injection you can read about Service Container for more info
I have created a simple aplication in Silex 1.3.4 and I want to have a base controller that will have a __construct method accepting $app and $request. All inheriting controllers then should have their respective constructors and calling the parent controller construct method.
//Use statements here....
class AppController
{
public function __construct(Application $app, Request $request){
$this->app = $app;
$this->request = $request;
}
}
Inheriting controllers would be written as below:
//Use statements here....
class ItemsController extends AppController
{
public function __construct(Application $app, Request $request){
parent::__construct($app, $request);
}
public function listAction()
{
//code here without having to pass the application and request objects
}
}
The approach I have decided on routing is as shown below:
$app->post(
'/items/list', 'MySilexTestDrive\Controller\ItemsController::listAction'
)->bind('list');
I was thinking of using the dispatcher and override some processes there and create my controller instances my own way but I do not have any idea how and if this is a great idea at all.
Anyone who has done something similar to this? Please help.
You can use ServiceControllerServiceProvider to define your controller as a service in the application. But you can't inject a Request in that way. BTW you can have more than one request and the request instance can change if you do sub-request. You can inject RequestStack instead, then call $requestStack->getCurrentRequest() when you need to get the current request.
$app = new Silex\Application();
abstract class AppController
{
protected $app;
protected $requestStack;
public function __construct(Silex\Application $app, Symfony\Component\HttpFoundation\RequestStack $requestStack)
{
$this->app = $app;
$this->requestStack = $requestStack;
}
public function getRequest()
{
return $this->requestStack->getCurrentRequest();
}
}
class ItemsController extends AppController
{
public function listAction()
{
$request = $this->getRequest();
// ...
}
}
$app->register(new Silex\Provider\ServiceControllerServiceProvider());
$app['items.controller'] = $app->share(function() use ($app) {
return new ItemsController($app, $app['request_stack']);
});
$app->get('/items/list', "items.controller:listAction");
It makes sense to do such a thing? I do not think so. Especially if the framework gives you a request instance thanks to the type hinting. Just do
public function listAction(Application $app, Request $request)
{
// ...
}
and work with that.
You can try this too :
class BaseController
{
protected $app;
protected $request;
public function __call($name, $arguments)
{
$this->app = $arguments[0];
$this->request = $arguments[1];
return call_user_func_array(array($this,$name), [$arguments[0], $arguments[1]]);
}
protected function getSystemStatus(Application $app, Request $request)
{
[...]
}
[...]
}
#Rabbis and #Federico I have come up with a more elegant solution for this where I have created a BeforeControllerExecuteListener that I dispatch with my application instance. This listener accepts the FilterControllerEvent object and then from my base controller I call a method where I inject both the Silex Application and the request from the event.
public function onKernelController(FilterControllerEvent $event)
{
$collection = $event->getController();
$controller = $collection[0];
if($controller instanceof BaseControllerAwareInterface){
$controller->initialize($this->app, $event->getRequest());
}
}
The I simple dispatch this in my bootstrap file as shown below:
$app['dispatcher']->addSubscriber(new BeforeControllerExecuteListener($app));
This allows me to have access to this object without having to add them as parameters on my actions. Below is how one of my actions in the making looks:
public function listAction($customer)
{
$connection = $this->getApplication()['dbs']['db_orders'];
$orders= $connection->fetchAll($sqlQuery);
$results = array();
foreach($orders as $order){
$results[$order['id']] = $order['number'] . ' (' . $order['customer'] . ')';
}
return new JsonResponse($results);
}
If the currently running controller being called honors the BaseControllerAwareInterface interface as I have defined it then it means I should inject that controller with the Application and Request instances. I leave the controllers to deal with how they manage the Response of each action as with my example above I may need the Response object itself of JsonResponse even any other type of response so it entirely depends on the controller to take care of that.
Then the routing remains as simply as:
$app->match('/orders/list/{cusstomer}', 'Luyanda\Controller\OrdersController::listAction')
->bind('list-orders');
Is it possible to inject a route-paramter (or an route segment) to the controller-constructor?
You find some code to clarify my question.
class TestController{
protected $_param;
public function __construct($paramFromRoute)
{
$this->param = $paramFromRoute;
}
public function testAction()
{
return "Hello ".$this->_param;
}
}
----------------------------------------------------
App::bind('TestController', function($app, $paramFromRoute){
$controller = new TestController($paramFromRoute);
return $controller;
});
----------------------------------------------------
// here should be some magic
Route::get('foo/{bar}', 'TestController');
It's not possible to inject them, but you have access to all of them via:
class TestController{
protected $_param;
public function __construct()
{
$id = Route::current()->getParameter('id');
}
}
Laravel 5.3.28
You can't inject the parameter...
But, you can inject the request and get it from the router instance, like this:
//route: url_to_controller/{param}
public function __construct(Request $request)
{
$this->param = $request->route()->parameter('param');
}
In Laravel 5.4, you can use this to request the parameter:
public function __construct(Request $request) {
$id = $request->get("id");
}
If you want a more testable solution, you can use Service Provider power.
$this->app->bind(TestController::class, function ($app) {
return new TestController(request()->testParam);
});
UPDATE FOR LARAVEL 8
you can use the route() method to get the value from the route url parameter from laravel 8:
$id = request()->route('id')
Lastly, but most importantly, you may simply "type-hint" the dependency in the constructor of a class that is resolved by the container, including controllers, event listeners, queue jobs, middleware, and more. In practice, this is how most of your objects are resolved by the container.
http://www.golaravel.com/laravel/docs/5.1/container/