I'm trying to do dependency injection in Laravel to keep my controllers and models as slim as possible. The goal is to have repositories to handle the fetching of data attributed to certain models.
To this end I'm trying to follow the example from the documentation here and a popular Laravel boilerplate here
But I don't understand where the $user is coming from.
So looking at the boilerplate we have two files:
The ProfileController here
Excerpt below:
use App\Repositories\Frontend\Access\User\UserRepository;
/**
* Class ProfileController.
*/
class ProfileController extends Controller
{
/**
* #var UserRepository
*/
protected $user;
/**
* ProfileController constructor.
*
* #param UserRepository $user
*/
public function __construct(UserRepository $user)
{
$this->user = $user;
}
This looks a lot like the dependency injection mentioned in the docs, which is this:
class UserController extends Controller {
/**
* The user repository instance.
*/
protected $users;
/**
* Create a new controller instance.
*
* #param UserRepository $users
* #return void
*/
public function __construct(UserRepository $users)
{
$this->users = $users;
}
My problem is I don't understand where the $user is coming from.
In the UserRepository there is no $user defined as a parameter of the class itself. No where in the code is there any Auth::user() so I'm confused as to where the user instance is coming from.
In Laravel dependency injection is handled by the Container. I'm simplifying, but you can think of the container as a source of objects. If there is a singleton, its stored in the container. Otherwise the container knows how to instantiate objects for you. Whenever Laravel calls a method (like in a controller) or instantiates an object for you it will inspect the constructor and look for type hinted dependencies. If it sees a dependency it knows how to retrieve or create it will do so and pass it in for you.
So when Laravel instantiates the controller it looks at the constructor
public function __construct(UserRepository $user)
{
$this->user = $user;
}
The container uses Type Hinting to see that it requires a UserRepository so it will instantiate a new one for you. It also does this recursively. So when it creates a new UserRepository it looks at that constructor and sees that it requires a RoleRepository so it will instantiate that as well.
TLDR: The service container inspects your dependencies and will instantiate them for you.
Welcome to the dubious magic of Laravel. The basic idea with these dependency injections is that, depending on how you define your routes & controllers, Laravel can perform some automagical parsing of urls, identification of ids in those urls, and database fetching of objects.
My problem is I don't understand where the $user is coming from.
You should probably read the docs on the service container. You can also get a better idea of how your route definitions translate into parameter-laden urls with this command:
php artisan route:list
On one of my projects, this results in this output:
+--------+-----------+----------------------------+--------------------+-------------------------------------------------+--------------+
| Domain | Method | URI | Name | Action | Middleware |
+--------+-----------+----------------------------+--------------------+-------------------------------------------------+--------------+
| | GET|HEAD | / | | Closure | web |
| | GET|HEAD | api/user | | Closure | api,auth:api |
| | GET|HEAD | categories | categories.index | App\Http\Controllers\CategoryController#index | web |
| | POST | categories | categories.store | App\Http\Controllers\CategoryController#store | web |
| | GET|HEAD | categories/create | categories.create | App\Http\Controllers\CategoryController#create | web |
| | GET|HEAD | categories/{category} | categories.show | App\Http\Controllers\CategoryController#show | web |
| | PUT|PATCH | categories/{category} | categories.update | App\Http\Controllers\CategoryController#update | web |
| | DELETE | categories/{category} | categories.destroy | App\Http\Controllers\CategoryController#destroy | web |
| | GET|HEAD | categories/{category}/edit | categories.edit | App\Http\Controllers\CategoryController#edit | web |
| | GET|HEAD | products | products.index | App\Http\Controllers\ProductController#index | web |
| | POST | products | products.store | App\Http\Controllers\ProductController#store | web |
| | GET|HEAD | products/create | products.create | App\Http\Controllers\ProductController#create | web |
| | GET|HEAD | products/{product} | products.show | App\Http\Controllers\ProductController#show | web |
| | PUT|PATCH | products/{product} | products.update | App\Http\Controllers\ProductController#update | web |
| | DELETE | products/{product} | products.destroy | App\Http\Controllers\ProductController#destroy | web |
| | GET|HEAD | products/{product}/edit | products.edit | App\Http\Controllers\ProductController#edit | web |
+--------+-----------+----------------------------+--------------------+-------------------------------------------------+--------------+
And all those routes and their uris and parameters are generated from only a couple of very simple routes definitions. Here's my routes file:
$ cat routes/web.php
<?php
Route::get('/', function () {
return view('master');
});
Route::resource('products', 'ProductController');
Route::resource('categories', 'CategoryController');
If you look at the list of URIs in the routes output above, you'll see parameters named in the URIs like {category} and {product}. These correspond to ids/keys in the URI which Laravel identifies. Laravel is "smart" enough to look at my Controller files, see the type-hinting in the various functions and detect that my function is expecting a depedency to be injected.
For instance, the Category controller's show method looks like this:
public function show(Tree $category)
{
var_dump($category);
}
My controller might seem a little unusual because I'm type hinting that I want an object of type Tree, but Laravel is smart enough to recognize that I do in fact want a Model of type Tree, so it parses out the url and finds the id in it and automatically fetches the record in my db table trees with id matching the {category} fragment of my url and injects that into my function.
Note that I had some trouble when I tried to name the input parameter $tree instead of $category. That other thread may help answer your question a bit too.
The bottom line is that Laravel does a lot of "magic" to hopefully free you up from the tedious of manually defining your own code and queries to retrieve the objects you want.
Related
i have url like this mysite.com/company, i want add prefix to url to become mysite.com/home/company.
I've tried to add a route group, but that requires me to update all existing routes.
can i add a prefix in all url without affecting the existing route ?
i used laravel 5.6
I have created a sandbox so that you can view and play around with the code used for this answer.
I know the sandbox uses a different Laravel version (version 7), but looking at the documentation for version 5.6 the routing does not seem to be that much different than that of version 7.
What you can do is wrap the already existing routes inside an anonymous function and assign it to a variable, you can then use this variable and pass it as a parameter to the group routing function along with a prefix, e.g.
$routes = function() {
Route::get('company', function () {
return 'companies';
});
Route::get('company/{company}', function ($company) {
return "company $company";
});
Route::delete('company/{company}', function ($company) {
return "deleting company $company...";
});
Route::get('company/{company}/staff', function ($company) {
return "staff list for company $company...";
});
};
Route::prefix('/')->group($routes);
Route::prefix('/home')->group($routes);
When running php artisan route:list the following is returned:
+--------+----------+------------------------------+------+---------+------------+
| Domain | Method | URI | Name | Action | Middleware |
+--------+----------+------------------------------+------+---------+------------+
| | GET|HEAD | api/user | | Closure | api |
| | | | | | auth:api |
| | GET|HEAD | company | | Closure | web |
| | GET|HEAD | company/{company} | | Closure | web |
| | DELETE | company/{company} | | Closure | web |
| | GET|HEAD | company/{company}/staff | | Closure | web |
| | GET|HEAD | home/company | | Closure | web |
| | GET|HEAD | home/company/{company} | | Closure | web |
| | DELETE | home/company/{company} | | Closure | web |
| | GET|HEAD | home/company/{company}/staff | | Closure | web |
+--------+----------+------------------------------+------+---------+------------+
You can see above that the routes can now be accessed via both / and home/, e.g. http://example.com/company and http://example.com/home/company without the need for duplicating routes.
If you need to add any more prefixes in the future you just simply add a new Route::prefix("<prefix>")->group($routes); to the routes file.
The update below is in response to the comment provided by OP.
To get this correct you are looking for a way to automatically convert all instances of the url function from url('someurl') to url('home/someurl'), e.g.
url('company') will become url('home/company')
url('knowledgebase') will become url('home/knowledgebase')
If so then I have two solutions for you:
Can you not simply do a search and replace within the IDE you are using?
You can override Laravel's url helper function to prefix all path's with home/, to do so you can do the following:
I have created another sandbox so that you can view and play around with the code used for this answer.
First create a helpers.php file anywhere within your Laravel application, when testing this code I placed the file in the root directory of the application, e.g. /var/www/html/helpers.php.
Second you need to override the url helper function, to make sure you don't lose any functionality I grabbed the original source code of the function from Laravel's github repository. I then modified it to include the prefix, so place the following in the new helpers.php file:
<?php
use Illuminate\Contracts\Routing\UrlGenerator;
if (! function_exists('url')) {
/**
* Generate a url for the application.
*
* #param string|null $path
* #param mixed $parameters
* #param bool|null $secure
* #return \Illuminate\Contracts\Routing\UrlGenerator|string
*/
function url($path = null, $parameters = [], $secure = null)
{
if (is_null($path)) {
return app(UrlGenerator::class);
}
$path = "home/$path";
return app(UrlGenerator::class)->to($path, $parameters, $secure);
}
}
Next, you need to load your helpers.php file before Laravel loads their helper functions, otherwise it wont load, to do so add require __DIR__.'/../helpers.php'; to public/index.php before require __DIR__.'/../vendor/autoload.php';, e.g.
require __DIR__.'/../helpers.php';
require __DIR__.'/../vendor/autoload.php';
Now you can use the new url function within your application, I have given some examples in the web.php routes file:
<?php
use Illuminate\Support\Facades\Route;
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
Route::get('/', function () {
echo url("company");
echo "<br/>";
echo url("knowledgebase");
});
The above will output to the webpage:
http://example.com/home/company
http://example.com/home/knowledgebase
Now if you need to change the url function slightly to more fit your requirements you can do so by modifying it within the helpers.php file.
I am new in Symfony and i am working on existing project. I have created crud with doctrine:generate:crud, but application returns me 404 page not found. I was debugging it with debug:router and router:match and everything was okay.
Here is my controller
<?php
namespace AppBundle\Controller\Backend;
use AppBundle\Controller\BackendController;
use AppBundle\Entity\CsobApiUsers;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\HttpFoundation\Request;
/**
* CsobApiUsers controller.
*
* #Route("csobApiUsers")
*/
class CsobApiUsersController extends BackendController
{
/**
* Lists all csobApiUser entities.
*
* #Route("/", name="csobapiusers_index")
* #Method("GET")
*/
public function indexAction()
{
die(var_dump("x"));
$em = $this->getDoctrine()->getManager();
$csobApiUsers = $em->getRepository('AppBundle:CsobApiUsers')->findAll();
return $this->render('csobapiusers/index.html.twig', array(
'csobApiUsers' => $csobApiUsers,
));
}
Controller is as same as another controller that works correctly.
Here is my router:match
php bin/console router:match /backend/csobApiUsers/
[OK] Route "csobapiusers_index" matches
+--------------+---------------------------------------------------------+
| Property | Value |
+--------------+---------------------------------------------------------+
| Route Name | csobapiusers_index |
| Path | /backend/csobApiUsers/ |
| Path Regex | #^/backend/csobApiUsers/$#s |
| Host | ANY |
| Host Regex | |
| Scheme | ANY |
| Method | GET |
| Requirements | NO CUSTOM |
| Class | Symfony\Component\Routing\Route |
| Defaults | _controller: AppBundle:Backend\CsobApiUsers:index |
| Options | compiler_class: Symfony\Component\Routing\RouteCompiler |
+--------------+---------------------------------------------------------+
Here is debug:router for my controller
csobapiusers_index GET ANY ANY /backend/csobApiUsers/
csobapiusers_new GET|POST ANY ANY /backend/csobApiUsers/new
csobapiusers_show GET ANY ANY /backend/csobApiUsers/{id}
csobapiusers_edit GET|POST ANY ANY /backend/csobApiUsers/{id}/edit
csobapiusers_delete DELETE ANY ANY /backend/csobApiUsers/{id}
I calling https://my-address.com/backend/csobApiUsers/
Does anybody know where can be problem? Thanks
I think you're missing slash in controller route annotation, check here https://symfony.com/blog/new-in-symfony-3-4-prefix-all-controller-route-names
#Route("/csobApiUsers")
Did you try changing your indexAction() to index() instead ?
I got this issue :
My first route disallow me of using any character that isn't a number (from the regex), this is perfectly working (tell me if you need screen of the result)
But the second one let me use any character as an id, I don't get why, I've tried clearing cache (and many other things) but it stills allow me to use alpha characters.
I have theses two routes currently :
<?php
// src/Controller/AdvertController.php
namespace App\Controller;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Twig\Environment;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
/**
* #Route("/advert")
*/
class AdvertController extends AbstractController
{
/**
* #Route("/{page}", name="oc_advert_index", requirements={"page" = "\d+"}, defaults={"page" = 1})
*/
public function index(Environment $twig, $page)
{
$content = $twig->render('Advert/index.html.twig', ['page' => $page, 'name' => 'alex']);
return new Response($content);
}
/**
* #Route("/view/{id}", name="oc_advert_view", requirements={"id" = "\d+"})
*/
public function view(Environment $twig, $id)
{
$content = $twig->render('Advert/view.html.twig', ['id' => $id, 'name' => 'alex']);
return new Response($content);
}
}
My templates are basic Twig templates (displaying either "id" or "page")
I've tried to look for hidden space and something but I can't find where is the difference (excluding the names "id" and "page")
EDIT1 : here what's I got when executing php bin/console router/match /advert/view/abc:
[OK] Route "oc_advert_view" matches
+--------------+---------------------------------------------------------+
| Property | Value |
+--------------+---------------------------------------------------------+
| Route Name | oc_advert_view |
| Path | /advert/view/{id} |
| Path Regex | #^/advert/view/(?P<id>[^/]++)$#sDu |
| Host | ANY |
| Host Regex | |
| Scheme | ANY |
| Method | ANY |
| Requirements | NO CUSTOM |
| Class | Symfony\Component\Routing\Route |
| Defaults | _controller: App\Controller\AdvertController::view |
| Options | compiler_class: Symfony\Component\Routing\RouteCompiler |
| | utf8: true |
+--------------+---------------------------------------------------------+
It seems that the "Requirements" are not detected/used for some reason, does someone know why ?
EDIT2: Here what I got when doing php bin/console router:match /advert/123
[OK] Route "oc_advert_index" matches
+--------------+---------------------------------------------------------+
| Property | Value |
+--------------+---------------------------------------------------------+
| Route Name | oc_advert_index |
| Path | /advert/{page} |
| Path Regex | #^/advert(?:/(?P<page>\d+))?$#sDu |
| Host | ANY |
| Host Regex | |
| Scheme | ANY |
| Method | ANY |
| Requirements | page: \d+ |
| Class | Symfony\Component\Routing\Route |
| Defaults | _controller: App\Controller\AdvertController::index |
| | page: 1 |
| Options | compiler_class: Symfony\Component\Routing\RouteCompiler |
| | utf8: true |
+--------------+---------------------------------------------------------+
Here we can see that it detects the regex, it feels like this is a typo error but I have copy/pasted at least 3 times, very weird :/
FINAL EDIT:
Thank you to every person who tried to find the issue, I learned a few things so I am very thankful (and sorry for the time waste^^)
RESOLVED : There were conflicts between config/routes.yaml and my routes, I had to delete the duplicated route. (I followed a course where we first declare it in config/routes.yaml and then we declare routes with annotations, forgot the step of deleting content of config/routes.yaml)
Thank you to every person who tried to find the issue, I learned a few things so I am very thankful (and sorry for the time waste^^)
I'm getting a 404 error when trying to access a route linked to a controller action.
I have the route defined like this in my routes.php file.
Route::controller('error', 'ErrorsController');
The ErrorsController class looks as follows.
class ErrorsController extends BaseController {
public function __construct()
{
// vacio
}
public function getIndex()
{
return View::make('error.accessdenied');
}
public function getAccessDenied()
{
return View::make('error.accessdenied');
}
}
I have a view with a link to chek if it is working properly. The link is created as follows
{{ HTML::linkAction('ErrorsController#getAccessDenied', 'Error') }}
When I click on the link the page moves to the URL 'mytestdomain.com/error/access-denied' returning an 404 error, but when I access the URL 'mytestdomain.com/error' it works perfectly.
Any idea on what I'm doing wrong?
EDIT:
Running the command php artisan routes these are the routes pointing to ErrorsController:
+--------+------------------------------------------------------------------------------------------------+------+--------------------------------------+----------------+---------------+
| Domain | URI | Name | Action | Before Filters | After Filters |
+--------+------------------------------------------------------------------------------------------------+------+--------------------------------------+----------------+---------------+
| | GET|HEAD error/index/{one?}/{two?}/{three?}/{four?}/{five?} | | ErrorsController#getIndex | | |
| | GET|HEAD error | | ErrorsController#getIndex | | |
| | GET|HEAD error/access-denied/{one?}/{two?}/{three?}/{four?}/{five?} | | ErrorsController#getAccessDenied | | |
| | GET|HEAD|POST|PUT|PATCH|DELETE error/{_missing} | | ErrorsController#missingMethod | | |
+--------+------------------------------------------------------------------------------------------------+------+--------------------------------------+----------------+---------------+
Only the sencond and the fourth ones are working.
It looks as though specifying the route in the way you have won't work. This type of routing only works for RESTful requests. See >http://laravel.com/docs/4.2/controllers#restful-resource-controllers>.
You might have to explicitly specify the route using Route::get/post.
Somehow I found the problem.
For some reason, my apache server doesn't rewrite mytestdomain.com/error/ * route. Probably is something related with the word error and the apache module mod_rewrite.
Anyway, defining the route as follows solves the problem.
Route::controller('fail', 'ErrorsController');
i have resource in route and that work correctly and i want to change that to Route::controller.
but after define that i get error in php artisan route :
+--------+------------------------------------+-----------+---------------------------------+----------------+---------------+
| Domain | URI | Name | Action | Before Filters | After Filters |
+--------+------------------------------------+-----------+---------------------------------+----------------+---------------+
| | GET index | index | Closure | | |
| | GET admin/index | dashboard | Closure | | |
| | GET logout | logout | Closure | | |
| | POST auth | auth | Closure | csrf | |
| | GET login | login | Closure | | |
| | GET admin/admin/profile/{_missing} | | ProfileController#missingMethod | | |
+--------+------------------------------------+-----------+---------------------------------+----------------+---------------+
my Current route is:
Route::resource('profile' , 'ProfileController', array('as'=>'profile') );
and i want to change that to :
Route::controller('admin/profile', 'ProfileController', array('index'=>'profile.index') );
how to resolve this problem?
This is not an error, Resource and Controller routes are completely different things.
Resource routes have a predefined list of routes (index, create, store, delete, update). If you don't have the method set in your controller it will still work, unless someone hit that route.
Controller routes relies on your controller methods:
public function getIndex() {}
public function getCreate() {}
public function postStore() {}
Methods names are predefined as
<http method><your action name>()
If those methods are not present in your controller, Laravel will not show them in your routes list.
So, just create a
public function getIndex() {}
In your controller and run
php artisan route
Again.
Use :
Route::resource('profile, 'ProfileController', array('as' => 'profile', 'names' => array('index' => 'profile.index')));
Instead of either the routes above.