Executing function before every controller request - laravel

I'm calling cloud APIs using token authentication with php-openstack-sdk.
$openstack = new OpenStack\OpenStack([
'authUrl' => '{authUrl}',
'region' => '{region}',
'user' => [
'id' => '{userId}',
'password' => '{password}'
],
'scope' => ['project' => ['id' => '{projectId}']]
]);
However, every API call requires me to be authenticated (as shown in the code above). Instead of repeating the same auth code in every controller function, how do I do it once and be able to call $openstack in my controller's functions? i.e., in my controller, I can directly use $openstack.
public function listServers()
{
$openstack->computeV2()->listServers();
}

Write the logic in the __construct() of your Controller.php if you want that to be accessible for all the controllers. If not, write the __construct() within the controller you need.
Controller.php
class Controller extends BaseController
{
protected $openstack;
public function __construct()
{
$this->openstack = new OpenStack\OpenStack([
...
]);
}
}
NetworkController.php
class NetworkController extends Controller
{
public function getNetworkDetails() {
$network = $this->openstack->networking();
}
}

You can place the code shown in the __construct function of your controller and provide it as a protected variable to the class.

I think the best way is to use laravel middlewares

Related

Why my resource do not return all meta data having "with" method?

In lumen 8 app I use resources and reading here
https://laravel.com/docs/8.x/eloquent-resources
I try to use “with” method, as I want to add some meta data to any request and I have no
this meta data in returned data :
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
use App\Models\Page As PageModel;
use App\Http\Resources\User as UserResource;
class Page extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'title' => $this->title,
...
'created_at' => $this->created_at,
];
}
public function with($request)
{
\Log::info( '-1 unction with ::' . print_r( 1, true ) ); // I DO NOT SEE THIS LOGGINHG line
return [
'meta' => [
'version'=>getAppVersion()
]
];
}
}
In the referenced docs resource is declared a bit different from ResourceCollection:
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\ResourceCollection;
class UserCollection extends ResourceCollection
{
public function toArray($request)
{
return parent::toArray($request);
}
public function with($request)
{
return [
'meta' => [
'key' => 'value',
],
];
}
}
Could it be the issue and how can fix my resource to get all meta data ?
Updated block:
UserCollection - that is collection https://laravel.com/docs/8.x/eloquent-resources
my collection is Page and I use it in controller as :
namespace App\Http\Controllers;
use Carbon\Carbon;
use App\Models\Page;
use Illuminate\Http\Request;
use App\Http\Resources\Page as PageResource;
use Config;
use App\Http\Requests\PageRequest;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Storage;
use Validator;
class PageController extends Controller
{
public function index()
{
$pages = Page
...
->get();
return $this->sendOkResponse(PageResource::collection($pages), '');
}
sendOkResponse defined in Http/Controllers/Controller.php :
class Controller extends BaseController
{
protected $requestData;
public function __construct()
{
$request = request();
$this->requestData = $request->all();
}
public function sendOkResponse($responseResult, $message)
{
$response = [
'success' => true,
'data' => $responseResult,
'message' => $message,
];
return response()->json($response, HTTP_RESPONSE_OK);
}
I suppose PageResource is destroyed at PageController controller index method exit...
Updated block # 2:
After some tests I found that Resource method “with” does not work if collection is returned
and I need to use ->additional in controller like:
return (PageResource::collection($pages))
->additional([
'meta' => [
'version' => getAppVersion()
]
]);
But in cases when I return sinopgle element(ex store method) like
return (new PageResource($page));
method “with” works ok.
That exludes using of wrapper like sendOkResponse.
Is is the only proper way?
Thanks in advance!
Laravel resources are intended to be returned directly from your controller's action method, not as part of an associative array representing JSON.
When wrapping your responses with the sendOkResponse method, the resource is not being returned directly from the method and thus toArray is being called on your resource. The with method on your resources is being ignored.
Try returning the resources directly from your controller's method. Use the additional method when constructing your resources to pass the extra attributes in the response. See: https://laravel.com/docs/8.x/eloquent-resources#adding-meta-data-when-constructing-resources.
If you can control the API contracts, I'd recommend changing them to omit success entirely, this can be derived from the HTTP status code.

Use 2 different FormRequests in a single controller

Is it possible to use 2 different FormRequest validations in a single Controller, one for store and the other for index, how?
I used method() to returns different validations from rules(), ex:
public function rules()
{
if($this->method() == 'GET')
{
return [
'customer' => 'required|numeric',
];
}
if($this->method() == 'POST')
{
return [
'author' => 'required|numeric',
];
}
}
but looks very uncomfortable
You can use 2 different Formrequest in one controller.
I do it as following
class PostController extends Controller
{
public function index(ManagePostRequest $request){
// your code goes here
}
public function create(CreatePostRequest $request){
// your code goes here
}
public function store(StorePostRequest $request){
// your code goes here
}
}
So according to the method you can have different rules in form request. Also you can use them for authorize the method.
Hope this is what you were asking as the question was a little unclear to me.

Laravel REST API and frontend

I created a project in Laravel, small database and added REST API in laravel to connect mobile app with database. What should I use to get data from database in web application? Using laravel models is easy but is that a good way to create another controllers to handle forms etc instead using rest api controllers? Thanks
Laravel also support for Restful API in own way.
for this
you create your controller in Api folder: php artisan make:controller Api/TestController
define your routes in routes/api.php :
Route::group(['namespace' => 'Api'], function (){
Route::group(['prefix' => '/test'], function () {
Route::get('/', 'TestController#list);
Route::get('/single', 'TestController#single');
});
});
create a resource collection for data that is an array of collection
php artisan make:resource Api/Collections TestCollection this command create a collection in folder app/Http/Resources/Api/Collections
open in and change toArray($request) function and add a function with($request) like following code :
public function toArray($request)
{
return $this->collection->map(function ($item){
return [
'id' => $item->id, // $item is instance of Test model
'name' => $item->name,
'description' => $item->description,
];
});
}
public function with($request) // optional : this method return with of response
{
return [
'status' => true
];
}
so go to TestController and create a method for get all tests:
public function list()
{
$tests = Test::all(); // your Test Model
return new TestCollection($test); // TestCollection you created above
}
this is return a json object that contains array of tests.
for get a single test :
php artisan make:resource Api/Resources TestResource
then go to TestResource in app/Http/Resources/Api/Collections and change code like following:
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name, // $this is instance of Test model
'description' => $this->description,
'body' => $this->body,
'diff_name' => $this->name_in_table // you can change the name differ from name in model instance
];
}
so go to TestController and create a method for single test
public function single(Request $request)
{
$test = Test::findOrFail($request->id);
return new TestResource($test);
}
this a summary of Rest API in laravel. Hope you find it useful
With laravel, you can reuse your api endpoints by taking advantage of the CreateFreshApiToken middleware.
Then you will only need to create new controllers and methods to display views. All of the CRUD stuff can be reused.

Laravel policy not working

In my Laravel application, i have several policies working, but one will not work.
Controller
public function store(Project $project, CreateActionRequest $request)
{
$this->authorize('store', $project);
Action::create([
'name' => $request->name,
]);
return redirect()->route('projects.show', $project->id)->withSuccess('Massnahme erfolgreich gespeichert');
}
Policy
namespace App\Policies\Project;
use App\Models\Project\Project;
use App\Models\User;
use App\Models\Project\Action;
use Illuminate\Auth\Access\HandlesAuthorization;
class ActionPolicy
{
use HandlesAuthorization;
public function store(User $user, Project $project)
{
return $user->company_id === $project->company_id;
}
}
AuthServiceProvider
protected $policies = [
'App\Models\User' => 'App\Policies\CompanyAdmin\UserPolicy',
'App\Models\Company' => 'App\Policies\CompanyAdmin\CompanyPolicy',
'App\Models\Team' => 'App\Policies\CompanyAdmin\TeamPolicy',
'App\Models\Department' => 'App\Policies\CompanyAdmin\DepartmentPolicy',
'App\Models\Location' => 'App\Policies\CompanyAdmin\LocationPolicy',
'App\Models\Division' => 'App\Policies\CompanyAdmin\DivisionPolicy',
'App\Models\Costcenter' => 'App\Policies\CompanyAdmin\CostcenterPolicy',
'App\Models\Workplace' => 'App\Policies\CompanyAdmin\WorkplacePolicy',
'App\Models\Product' => 'App\Policies\CompanyAdmin\ProductPolicy',
'App\Models\Project\Action' => 'App\Policies\Project\ActionPolicy',
'App\Models\Project\Project' => 'App\Policies\Project\ProjectPolicy',
];
CreateActionRequest
namespace App\Http\Requests\Project;
use Illuminate\Foundation\Http\FormRequest;
class CreateActionRequest extends FormRequest
{
public function authorize()
{
return true;
}
public function rules()
{
return [
'name' => 'required|min:3',
];
}
}
All policies are working except ActionPolicy and ProjectPolicy.
I added in the policy a __construct() method to check if the policy is called. But ActionPolicy and ProjectPolicy are not working.
How can i search the error? I tried with dd() but i got only allways the message: This action is unauthorized
Since you are injecting CreateActionRequest instead of Request that means you are defining your own set of rules to authorize the FormRequest which comes inside of your method. Further it means that you gotta define a few rules which the "FormRequest" has to pass in order to EVEN reach your controller, this is a nice concept that I like about Laravel since the code is not centralized, but rather spread and every layer has it's own responsibility. Now, you don't have to call any method from your CreateActionRequest nor you have to write any code regarding that class in your controller, because Laravel runs authorize method by default before allowing the Request to reach your controller, before running authorizemethod in your CreateActionRequest it runs rules method which verifies that all the given fields pass the expressions you assigned them, so the execution is something like this CreateActionRequest => rules => authorize => IF(authorized) Controller ELSE Not authorized, hope that makes sense. In order to fix your code:
1.) Remove $this->authorize('store', $project);
This will allow you to pass not authorized error in case your name passes the truth test inside of rules method inside of your CreateActionRequest. If you wish to utilize your Action Policy you will need to hook up your custom Request(CreateActionRequest) with it and this is how:
public function authorize()
{
$store = $this->route('project');
//The above line will return Project object if your mapping is correct
//If it's not it will return the value you passed to your route for {project}
return $this->user() && $this->user()->can('store', $store);
}
EDIT:
Here is the link where you can see how to properly authorize and connect policy with CreateActionRequest
Do you have all your controller methods defined with the Request object last?
public function store(Project $project, CreateActionRequest $request)
The Request object should be the first parameter in the methods signature:
public function store(CreateActionRequest $request, Project $project)
Dependency Injection & Route Parameters
If your controller method is also expecting input from a route parameter you should list your route parameters after your other dependencies.
Most Laravel authorization mechanisms have identical method signatures allowing them to work across varying classes.

Laravel share Auth::User() info

I made a BaseController to share user information but that doesn't work. Auth::user()->id and Auth::user()->email are empty.
How can I archive this? Whats the best approach?
class BaseAdminController extends Controller{
public function __construct()
{
$this->initMenu();
}
private function initMenu()
{
View::share('userinfo', (object) ['id' => Auth::User()->id, 'email' => Auth::User()->email]);
}
}
You can do it via middleware because you can't access the session or authenticated user in the controller's constructor, since the middlware isn't runnig yet:
class BaseAdminController extends Controller{
public function __construct()
{
$this->middleware(function ($request, $next) {
$this->initMenu();
return $next($request);
});
}
private function initMenu()
{
View::share('userinfo', (object) ['id' => Auth::User()->id, 'email' => Auth::User()->email]);
}
}
You can use the view helper function to share stuff easily. Maybe this will help you:
view()->share('userinfo', [...data to pass in an array...]);
or
view()->share('user', Auth::user());
This is an example! Only pas the info you want to the view, like you did in your example. But just pass the array, not casting it to object, etc....

Resources