I want to add multilanguage functionality to existing laravel project. Currently I have the following route:
$user_route = function() {
....
};
Route::group([
'prefix' => ''
], $user_route
);
Route::group([
'prefix' => '{language}',
'middleware' => 'set.lang'
], $user_route
);
Each anchor uses route() helper from Laravel. Since I have route with 'language' prefix, I need to pass $language from every route(). There are lot of route() and I don't think updating every route to route('name', ['language' => \App::getLocale()]) is a good option. The project already have Helper class and I plan to override route() method from Illuminate\Routing\UrlGenerator with my custom method. This is what I have tried :
Functions.php
use Illuminate\Routing\UrlGenerator as Url;
....
function route($name, $parameters = [], $absolute = true)
{
$parameters = array_add($parameters, 'language', \App::getLocale());
$url = new Url();
return $url->route($name, $parameters, $absolute);
}
Which obviously return error :
FatalErrorException in Functions.php line 158:
Cannot redeclare route() (previously declared in
C:\...\vendor\laravel\framework\src\Illuminate\Foundation\helpers.php:693)
I read about Service Container but as far as my understanding, only used to manage class dependencies and performing dependency injection. How to handle this correctly? Thank you very much for your help.
Related
I am trying to use this package to push notifications to users via OneSignal. However I needed to make a little change. My API serves two (related) apps and I have two OneSignal configs. I am trying to override its ServiceProvider (using this technique).
The ServiceProvider presents itself as follows
<?php
namespace NotificationChannels\OneSignal;
use Berkayk\OneSignal\OneSignalClient;
use Illuminate\Support\ServiceProvider;
use NotificationChannels\OneSignal\Exceptions\InvalidConfiguration;
class OneSignalServiceProvider extends ServiceProvider
{
/**
* Bootstrap the application services.
*/
public function boot()
{
$this->app->when(OneSignalChannel::class)
->needs(OneSignalClient::class)
->give(function () {
$oneSignalConfig = config('services.onesignal');
if (is_null($oneSignalConfig)) {
throw InvalidConfiguration::configurationNotSet();
}
return new OneSignalClient(
$oneSignalConfig['app_id'],
$oneSignalConfig['rest_api_key'],
''
);
});
}
}
The behavior that I want to change is located in the line
$oneSignalConfig = config('services.onesignal');
As it assumes that my config/services.php has the following entry (stated in the doc) :
// config/services.php
...
'onesignal' => [
'app_id' => env('ONESIGNAL_APP_ID'),
'rest_api_key' => env('ONESIGNAL_REST_API_KEY')
],
...
Whereas I want to set my config/services.php as follows
// config/services.php
...
'onesignal' => [
'app1' => [
'app_id' => env('ONESIGNAL_1_APP_ID'),
'rest_api_key' => env('ONESIGNAL_1_REST_API_KEY')
],
'app2' => [
'app_id' => env('ONESIGNAL_2_APP_ID'),
'rest_api_key' => env('ONESIGNAL_2_REST_API_KEY')
],
],
...
And I want somehow to tell my ServiceProvider (through some kind of parameter) to either do
$oneSignalConfig = config('services.onesignal.app1');
OR
$oneSignalConfig = config('services.onesignal.app2');
But I didn't find any way to pass a parameter to the class, the boot function or the give method (and if I understood well I shouldn't even be doing that).
The only way I could think of is to create two classes that extend the OneSignalChannel::class
and duplicate code in the boot function so it becomes as follows :
public function boot()
{
$this->app->when(FirstOneSignalChannel::class)
->needs(OneSignalClient::class)
->give(function () {
$oneSignalConfig = config('services.onesignal.app1');
if (is_null($oneSignalConfig)) {
throw InvalidConfiguration::configurationNotSet();
}
return new OneSignalClient(
$oneSignalConfig['app_id'],
$oneSignalConfig['rest_api_key'],
''
);
});
$this->app->when(SecondOneSignalChannel::class)
->needs(OneSignalClient::class)
->give(function () {
$oneSignalConfig = config('services.onesignal.app2');
if (is_null($oneSignalConfig)) {
throw InvalidConfiguration::configurationNotSet();
}
return new OneSignalClient(
$oneSignalConfig['app_id'],
$oneSignalConfig['rest_api_key'],
''
);
});
}
The difference in the when provoking a difference in the config but it seems a lot of duplication and not extensible (what if I had three apps).
Should I use this method, or is there a way to pass a parameter to this ServiceProvider or is there another solution ?
https://stackoverflow.com/a/34224082/10371024
I could see what you need, but to pass parameter to boot method is not a good idea according to Laravel architecture. You may try to get what you want with using events as Vladislav suggested.
Hi I have the following Lumen Route
$router->get('/end', function (\Illuminate\Http\Request $request) use ($router) {
$controller = $router->app->make('App\Http\Controllers\Legacy\EndLegacyController');
return $controller->index($request);
});
I am trying to give it a route name so that I can use redirect to redirect to this route like redirect()->route('name_of_route')
so far I have tried
})->namedRoute['end'] = '/end'; // No Effect
})->name('end') //Undefined function
but it didn't work
here's a list of present routes
Edit
Because of my certain requirement, I can't use ['as' => 'end', 'uses'=> 'ControllerName#Action']
you can use the following syntax: $router->get('/end', ['as'=>'name_here', function()]);
I am trying to use a method to implement accept/reject since I will be updating the same model with just little difference.
How can I pass a parameter to a controller method directly from Laravel Routes (i.e. instead of being supplied via url).
E.g. something like:
Route::put('/friends/{friend}/reject', ['as' => 'friends.reject', 'uses' => 'FriendsController#accept', 'accept' => false]);
Route::put('/friends/{friend}/accept', ['as' => 'friends.reject', 'uses' => 'FriendsController#accept']);
My controller code looks like:
public function accept($friend, $accept = true)
{
...
Is it possible to define the $accept parameter inside the Routes config file?
I have an improvement over the answer you found yourself. By adding a route constraint you make sure that only the two verbs are allowed.
Also I changed the naming to make more sense. If it doesn't, just change it ;)
Route::put('/friends/{friend}/{verb}',
['as' => 'friends.respond-to-request',
'uses' => 'FriendsController#respondToRequest']
)->where('verb', '(accept|reject)');
You can use this solution:
Route::put('/friends/{friend}/reject',array('as' => 'friends.reject',function($friend){
$accept = false;
$app = app();
$controller = $app->make('FriendsController');
$controller->callAction($app, $app['router'], 'accept', $parameters = array($friend, $accept));
}));
This code uses a Closure. In this closure the controller will be dispatched by laravel's router.
I finally resorted to:
Route::put('/friends/{friend}/{verb}', ['as' => 'friends.reject', 'uses' => 'FriendsController#accept']);
public function accept($friend, $verb = 'accept')
{
...//check if `verb` is 'accept' or 'reject' here..
I am not sure this is an ideal solution though, maybe it's better to implement with integer but it's possible Laravel processes both the same way.
So the title describes my problem pretty well I think, but let me explain why I want to do this as theremight be an other solution to my problem that I haven't thought about.
Let's say that I have a route specifying the class of the object it will patch:
Route::patch('{class}/{id}', array(
'as' => 'object.update',
function ($class, $id) {
$response = ...;
// here I want to call the update action of the right controller which will
// be named for instance CarController if $class is set to "car")
return $response;
}
));
This is something pretty easy to do with $app->make($controllerClass)->callAction($action, $parameters); but doing it this way won't call the filters set on the controller.
I was able to do it with laravel 4.0 with the callAction method, passing the app and its router, but the method has changed now and the filters are called in the ControllerDispatcher class instead of the Controller class.
If you have routes declared for your classes then you may use something like this:
$request = Request::create('car/update', 'POST', array('id' => 10));
return Route::dispatch($request)->getContent();
In this case you have to declare this in routes.php file:
Route::post('car/update/{id}', 'CarController#update');
If you Use this approach then filters will be executed automatically.
Also you may call any filter like this (not tested but should work IMO):
$response = Route::callRouteFilter('filtername', 'filter parameter array', Route::current(), Request::instance());
If your filter returns any response then $response will contain that, here filter parameter array is the parameter for the filter (if there is any used) for example:
Route::filter('aFilter', function($route, $request, $param){
// ...
});
If you have a route like this:
Route::get('someurl', array('before' => 'aFilter:a_parameter', 'uses' => 'someClass'));
Then the a_parameter will be available in the $param variable in your aFilter filter's action.
So I might have found a solution to my problem, it might not be the best solution but it works. Don't hesitate to propose a better solution!
Route::patch('{class}/{id}', array(
'as' => 'object.update',
function ($class, $id) {
$router = app()['router']; // get router
$route = $router->current(); // get current route
$request = Request::instance(); // get http request
$controller = camel_case($class) . 'Controller'; // generate controller name
$action = 'update'; // action is update
$dispatcher = $router->getControllerDispatcher(); // get the dispatcher
// now we can call the dispatch method from the dispatcher which returns the
// controller action's response executing the filters
return $dispatcher->dispatch($route, $request, $controller, $action);
}
));
I'm unit testing my application and I get a NotFoundHttpException when trying to call controller actions with TestCase::callSecure() and the route() helper.
For those tests (only a few) I have also enabled filters with Route::enableFilters.
My filters.php:
App::before(
function ($request) {
// force ssl
if (!$request->isSecure())
{
return Redirect::secure($request->getRequestUri());
}
}
);
// some authentication
Route::filter('auth.api', 'Authentication');
My routes.php:
Route::post('login', array('as' => 'login', 'uses' => 'AuthenticationController#login'));
Route::post('register', array('as' => 'register', 'uses' => 'AuthenticationController#register'));
Example Test where i get the exception:
$credentials = [
// ...
];
$response = $this->callSecure('POST', route('login'), $credentials);
When i call those actions by their path, it works fine.
$credentials = [
// ...
];
$response = $this->callSecure('POST', '/login', $credentials);
Is this intended or a bug?
The route() helper will generate a URL (including the relevant protocol, i.e. http/s) for the given named route. In your case, it'll return something like:
https://example.com/login
Which isn't what you want. This is useful when you want to perform a redirect, for example:
Redirect::route('login');
So what you're doing in your last example, is the correct way to be doing what you want; as you don't want to pass the full URL as a parameter to your callSecure() function.
$response = $this->callSecure('POST', '/login', $credentials);
And as dave has mentioned, you can generate a relative URL by using URL::route and pass an $absolute parameter which defaults to true. For example, when using named routes you can use the following:
$route = URL::route('profile', array(), false);
Which will generate a relative URL like /profile
The route() helper does not create relative URLs, which is what you really want.
To generate a relative URL, you can use URL::route as it allows you to pass a $absolute parameter which defaults to true. So to get your relative url using a named route, you could do
$credentials = [
// ...
];
$route = URL::route('login', array(), false);
$response = $this->callSecure('POST', $route, $credentials);
While the '/login' method is correct, it defeats the purpose of using named routes if you still have to hunt down all the places where you have that URL if/when you decide to change it.