I am making a project management application in Larvel. TaskController#index queries the db and return tasks. To be efficient and elegant, I want to be able to pass it several, optional, key/value pairs in the URL, like /tasks/org_id/36/status/open or /tasks/proj_id/1557/status/closed, and have it return the tasks based on those variables. My code is below, but the problem is getting the route to be able to receive the optional key/value pairs. Also, they shouldn't all have to all be submitted all of the time if they aren't needed.
Route/web.php:
Route::get('/tasks/status/{status}/proj_id/{proj_id}/user_id/{user_id}/org_id/{org_id}
/creator_id/{creator_id}', 'TaskController#index')->name('tasks.index');
Route::resource('tasks', 'TaskController')->except([
'tasks.index'
]);
Controller:
class TaskController extends Controller
{
public function index($proj_id = null, $recipient_id = null, $org_id = null, $creator_id = null, $status = null)
{
$tasks = Task::where('recipient_id', auth()->user()->id)
->when($status, function ($query, $status) {
return $query->where('status', $status);
})
->when($recipient_id, function ($query, $recipient_id) {
return $query->where('recipient_id', $recipient_id);
})
->when($public, function ($query, $public) {
return $query->where('public', $public);
})
->get();
return view('tasks.index', compact('tasks'));
}
How do I get the route to be able to accept a variety of optional key/value pairs?
For your convenience, work with GET (?status=...&...=...) parameters and work through them in a global middleware. It will possibly eliminate a lot of confusion as your project grows.
In the middleware you could do something like this:
public function handle($request, Closure $next)
{
$params = array();
//OR look them up individually:
$params['status'] = $request->query('status');
$params['proj_id'] = $request->query('proj_id');
$params['org_id'] = $request->query('org_id');
//OR get all query requests at once:
$params = $request->query();
//and set them as a session value
$request->session()->put('params', $params);
return $next($request);
}
Access the possible values anywhere in the project with the helper session('params')['status']. If there is no value in the url, it's defaulted to null.
Addition: to help you out building the query params for the url you may want to have a look at the PHP function http_build_query()
try this:
i think fix your problem
Route::resource('tasks', 'TaskController')->except([
'index'
]);
Route::get('/tasks/status/{status}/proj_id/{proj_id}/user_id/{user_id}/org_id/{org_id}
/creator_id/{creator_id}', 'TaskController#index');
i hope help you
https://laracasts.com/discuss/channels/laravel/routeresource-parameters
Related
I would like to recover the slug of 2 categories from my routes but can’t write the Controller.
My Route
Route::get('technicians/o/{occupation}/c/{city}', 'User\TechnicianController#viewoccupationcity');
My Controller
public function viewoccupationcity($slug)
{
$technicians = TechnicianResource::collection(occupation::where('slug',$slug)->firstOrFail()->technicians()
->with('city','occupation')
->latest()->get());
return $technicians;
}
Route::get('technicians/o/{occupation}/c/{city}', 'User\TechnicianController#viewoccupationcity');
Your controller will accept the parameters from your route as variables by order
public function viewoccupationcity($ocupation, $city)
{
...
}
Example:
URL: technicians/o/foo/c/bar
public function viewoccupationcity($ocupation, $city)
{
// $ocupation will be 'foo'
// $city will be 'bar
}
Ok, you would need to retrieve 2 variables as that is what you are passing
public function viewoccupationcity($occupation, $city)
If you want the whole slug to do another search then you would use the $request object. So like so
public function viewoccupationcity(Request $request, $occupation, $city){ // You also need to include the Request decleration
$slug = $request->path();
$technicians = TechnicianResource::collection(occupation::where('slug',$slug)->firstOrFail()->technicians()
->with('city','occupation')
->latest()->get());
return $technicians;
}
EDIT: We are having to do a lot of guesswork as your question isn't very clear. I think what you are trying to achieve is probably this
public function viewoccupationcity($occupation, $city){
$technicians = TechnicianResource::collection(occupation::where('city',$city)->where('occupation',$occupation)->firstOrFail()->technicians()
->with('city','occupation')
->latest()->get());
return $technicians;
}
If you need something more then you need to give more details
I have basic custom validation rule. In
public function passes($attribute, $value)
{
foreach ($parameters as $key)
{
if ( ! empty(Input::get($key)) )
{
return false;
}
}
return true;
}
I have my rule defined. I, although need to retrieve parameters from the rule but the passes method does not provide it as an argument.
If I would use the style Validator:extends... that provides 4 arguments: $attribute, $value, $parameters, $validator. Then I could use the parameters easily, unfortunatelly I have to use this way.
EDIT:
To clear the question. I want to retrieve the parameters of the rule, like so in other way of coding it:
'not_empty:user_id'. The array of values behind the colon.
Edit:---
The custom rule object is simply an object. If you want to pass it any more parameters you can in it's constructor:
$request->validate([
'name' => ['required', new MyCustomRule('param', true, $foo)],
]);
Then save those and use them in your passes function.
public function __construct($myCustomParam){
$this->myCustomParam = $myCustomParam;
}
Then in your passes function use:
$this->myCustomParam
I believe the only way is to retrieve it from the request when using rule objects.
For example:
public function passes($attribute, $value)
{
foreach ($parameters as $key) {
// Or using \Request::input($key) if you want to use the facade
if (!empty(request()->input($key)) {
return false;
}
}
return true;
}
I'm missing something with how the global scopes work in Laravel 5.5.
In my controller, index , I am passing filters into a getter:
public function index(SaleFilters $filters)
{
return new SaleCollection($this->getSales($filters));
}
getSales:
protected function getSales(SaleFilters $filters)
{
$sales = Sale::with('office')->filter($filters);
return $sales->paginate(50);
}
protected function range($range)
{
$dates = explode(" ", $range);
if (count($dates) == 2) {
$this->builder = Sale::with(['office', 'staff'])
->where('sale_date', '>=', $dates[0])
->where('sale_date', '<', $dates[1])
->orderBy('sale_date', 'desc');
return $this->builder;
}
return false;
}
I have a scope setup in the sale model as such, which I would have thought would apply to the above filter automatically ? If not, do I have to reapply the same scope, duplicating the scope code in the filter ?
protected static function boot()
{
parent::boot();
$user = Auth::user();
if (($user) && ($user['office_id'])) {
return Sale::ofOffice($user['office_id'])->get();
}
}
public function scopeOfOffice($query, $office)
{
return $query->where('office_id', $office);
}
So basically, IF the user has an office_id applied, it should apply the ofOffice scope, therefore it should only ever return the sales that apply to that office_id.
Basically it works on page load via axios GET request
Route::get('/sales', 'SalesController#index')->middleware('auth:api');
axios
.get('api/sales/?range=" + this.rangeFilter)
rangeFilter is basically a start and end date passed into the above filter query.
Can anyone shed some light on how the scopes really work or if anything is obvious as to why its not always working? As I said, it works on page load where I provide default values for the rangeFilter, however when I change those days and it refetches via the same axios call, it seems to not be applying the scope, and I get ALL results instead of where office_id = 'x'
As far as i'm concerned, the range filter above would be executing on the first page load as well, so not sure why it would apply there, and not afterwards.
You should not mix the use of dynamic scope with global one. Also, static boot function does not expect a return. In order to use dynamic scope, you need to call it every time you need it. Hence, the name is dynamic. Query applied is not always executed by default. There so,
protected function getSales(SaleFilters $filters)
{
$sales = Sale::ofOffice($anyOfficeHere)->with('office')->filter($filters);
return $sales->paginate(50);
}
To suit your existing code, you may want to add an if statement in your model. Then call the scope function without argument.
public function scopeOfOffice($q)
{
if (($user = \Auth::user()) && ($office = $user->office_id)) {
$q->where('office_id', $office);
}
}
// Your controller
protected function getSales(SaleFilters $filters)
{
$sales = Sale::ofOffice()->with('office')->filter($filters);
return $sales->paginate(50);
}
If you feel so much cumbersome to type ofOffice repeatedly. A global scope is the way to go. Within your model static boot function, you can also apply anonymous function if you feel creating a separated class kinda bloat your apps.
protected static function boot()
{
parent::boot();
static::addGlobalScope('officeOrWhatNot', function ($q) {
if (($user = \Auth::user()) && ($office = $user->office_id)) {
$q->where('office_id', $office);
}
});
}
// Your controller. No more `ofOffice`, it's automatically applied.
protected function getSales(SaleFilters $filters)
{
$sales = Sale::with('office')->filter($filters);
return $sales->paginate(50);
}
I try to pass a variable based on a cookie value in my compose function to all my view to build my menu, with the use of serviceproviders recommmended here:
File: Providers/ViewComposerServiceProvicer.php
public function boot(Request $request) { $this->composeTopBar($request);}
public function composeTopBar(Request $request)
{
$cookieValue = $request->cookie('brand');
// if value not set use default value.
if($cookieValue == null)
{
$cookieValue = 1;
}
$brands = \App\Brand::orderBy('priority', 'asc')->get();
foreach($brands as $brand){
if($brand->id == $cookieValue){
$brand->menuActive = true;
}
else{
// show value to debug
$brand->menuActive = $cookieValue;
}
}
view()->composer('front.layouts.top', function ($view) use ($brands) {
$view->with('brandItems',$brands );
});
}
the cookieValue looks like
yJpdiI6IlNJODBvQ1RNM004OWVleyJpdiI6IlNJODBvQ1RNM004OWVleyJpdiI6IlNJODBvQ1RNM004OWVl
While the value in my controller looks like '2' How can i get the original value 2 in my compose function?
I need to get the original value to compare it in my composeTopBar function so I can pass a variable to be true if it equals the cookie value.
Method to set cookie
$response = response()-> view('front.products.category', compact('products','category'));
$response->withCookie(cookie()->forever('brand',1));
return $response;
I ended up using a class based composer .
The reason why this works is because it's called later in the lifecycle of laravel and the Cookie variables are decrypted. When using Closure based composers the values are encrypted.
Try this: put the view() call as a parameter to response().
$response = response(view('front.products.category', compact('products','category')));
$response->withCookie(cookie()->forever('brand', 1));
return $response;
In laravel, if I want to pass parameters to a controller in my route file
Route::get('user/sk/{id}' , 'UsersController#findsk');
If I want to pass default parameters:
Route::get('user/{name?}', function ($name = 'John') {
// how do I invoke my controller here?
return $name;
});
How do I merge the two things? Is there a shortcut?
Route::get('user/sk/{id}' , 'UsersController#findsk'
// can I add an array of default parameters here?
);
As far as I know, there is no shortcut, unfortunately.
To inject one optional parameter:
Route::get('user/{name?}', function ($name = 'John') {
$ctrl = new \App\Http\Controllers\UsersController();
return $ctrl->findsk($name);
});
To inject one optional parameter with more parameters:
Assuming you had an $id param and that UsersController#findsk accepts $id and $name.
Route::get('user/{id}/{name?}', function ($id, $name = 'John') {
$ctrl = new \App\Http\Controllers\UsersController();
return $ctrl->findsk($id, $name);
});
To inject something in the controller from the router:
Assuming want to use a url as a switch.
Route::get('my-special-url', function () {
$ctrl = new \App\Http\Controllers\UsersController();
return $ctrl->findsk(1, 'Paul');
});
You can pass as a default parameter to function in your controller like just normal function
Route::get('user/sk/{id}' , 'UsersController#findsk');
in UsersController
function findsk($id ='myVal'){
}