I am sorry for asking such a question and I know there have been questions like this before, but my case is rather...stupid. What I have is a project management system and I want to display all projects which works fine and is auto generated by Symfony. In the same controller which is ProjectController I made another action. I wanted to list all archived projects which have been completely done. For a start I simply copy pasted the code and the annotations and changed the routes and the name of the function. Here is my indexAction function
/**
* Lists all project entities.
*
* #Route("/", name="project_index")
* #Method("GET")
*/
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$projects = $em->getRepository('AppBundle:Project')->findAll();
return $this->render('project/index.html.twig', array(
'projects' => $projects,
));
}
It is as simple as it can get. Now here is my archiveAction function which is the same
/**
* Lists all project entities.
*
* #Route("/archive", name="project_archive")
* #Method("GET")
*/
public function archiveAction()
{
$em = $this->getDoctrine()->getManager();
$projects = $em->getRepository('AppBundle:Project')->findAll();
return $this->render('project/index.html.twig', array(
'projects' => $projects,
));
}
So far I am not even filtering the projects, but doing the exact same thing as I do in the indexAction function and bear in mind both functions are in the same controller. Here is the error I receive when trying to show all archived projects -
AppBundle\Entity\Project object not found.
The index action works just fine and displays all projects, but if I change the route and change the name of the function and keep everything the same and they are in the same Controller - in one case it can find AppBUndle\Entity\Zadanie, but in the other case - it can't.
SOLVED I managed to solve the problem by putting the archive function on top. By on top I mean in first position before any other function. Before doing so I tried switching the routes. I took the route from indexAction and put it in the annotations from the archiveAction and the opposite. Both functions worked just fine. Then I returned them as they were and archive still didn't work. Then I simply put archive in first place before index and it worked as a charm. I have no idea what just happened and why but...didn't matter...it works. ( seriously though - if anybody knows why that is I would appreciate it)
EDIT: So 2 years and 4 months later I think I got the crank of it, the routes are cached so what I did was just clear the cache and it worked, but at the time I didnt pay attention to this detail
I found the same problem and I managed to find the cause of the problem.
routing problem, for example:
#Route ("/ {id}", name = "project_archive_show")
function2
#Route ("/ archive", name = "project_archive")
function1
urls must have personalized specifications:
for example :
#Route ("/ show / {id}", name = "project_archive_show")
function2
or says that id is an integer
Related
Route::get('/atomic/{id}',[ApiController::class,'index'])->defaults('task', 'atomic');
why use defaults here and what is a task & atomic, and Api controller does not have an index function. Please explain this route properly.
I am new to laravel I tried to google for a solution but no result
defaults method helps to pass extra params to controller without passing as route params
As a backend engineer you’ll often be asked to produce URL patterns
that just don’t work with the rest of the site without breaking your
current routing structure. Often you’ll create what’s known as a slug
for your content, a simple hyphen separated string which is unique in
the system. A typical slug would be just generated from the title like
“My Simple Article” becomes as a slug my-simple-article. This way
there’s a unique string in the system for each post.
If you’ve already been implementing routes like this in your system
you’ll likely have urls that look like /post/{slug} but you know now
that’s not going to be good enough. Your company’s marketing team or
SEO wizards want it to be /{slug} and that’s pretty tricky. You can’t
create the pattern /{post-slug} because it’s going to confuse the
system. What is you have an About Us page or a Contact Us page which
equally important urls like /about-us and /contact-us respectively.
The problem here being that the routing system might pick up the
/about-us link and believe it’s meant to be a slug for a Post model.
At this point Laravel will simply not find the model and throw a HTTP
404 error instead. Not good.
This is where the ‘defaults’ method on routes comes into use to save
the day.
if I consider your example then
Route::get('/atomic/{id}',[ApiController::class,'index'])->defaults('task', 'atomic');
while hitting URL http://127.0.0.1:8002/atomic/1 then in the controller,you will get both params $id and $task
public function index($id,$task){
dump($task);
dump($id);
}
the output of the above will be atomic and 1
defaults() method nothing but key-value pair params
/**
* Set a default value for the route.
*
* #param string $key
* #param mixed $value
* #return $this
*/
public function defaults($key, $value)
{
$this->defaults[$key] = $value;
return $this;
}
suppose if you want to pass multiple array params then use setDefaults method like below
Route::get('/atomic/{id}',[ApiController::class,'index'])->setDefaults([
'tasks'=> 'atomics',
'postTitle'=>'post title goes here'
]);
then in controller
public function index($id,$tasks,$postTitle){
dump($tasks);
dump($postTitle);
dump($id);
}
now if you hit URL http://127.0.0.1:8002/atomic/1 then it will print
atomics
post title goes here
1
Ref : The Power of Laravel’s Route ‘defaults’ for making root level SEO pages
Within EasyAdmin for Symfony you can use the AdminUrlGenerator for easily generating URLs to for example EasyAdmin CRUD Controllers.
Documentation here: https://symfony.com/doc/current/EasyAdminBundle/crud.html#generating-admin-urls
In my case i want to generate an URL to CRUD controller which is also linked within the Dashboard. If i create a link to a CRUD controller, the link works, but the corresponding MenuItem is not highlighted.
I found out, that EasyAdmin highlights the MenuItem with an URL parameter calling menuIndex. I can unset the menuIndex during Link Generation, but then no menuItem is highlightd in the menu.
I havn't found any information on how to get the correct menuIndex for a generated CRUD URL.
So how can i generate Admin URLs with correct menuIndex?
I want to give one possible answer. But to be honest, i don't really like my solution. Maybe someone has a better solution.
So, i am iterating over the configured menuItems within the DashboardController and trying to determine the menuIndex for a given Entity.
The code lookes like this:
private function getIndexLinkForCrudController(string $controller): string
{
return $this->adminUrlGenerator
->unsetAll()
->setController($controller)
->setAction(Action::INDEX)
->set('menuIndex', $this->determineMenuIndexForEntity($controller::getEntityFqcn()))
->generateUrl();
}
private function determineMenuIndexForEntity(string $entity): ?int
{
$menuItems = $this->configureMenuItems();
foreach ($menuItems as $id => $menuItem) {
/* #var MenuItemInterface $menuItem */
$routeParameter = $menuItem->getAsDto()->getRouteParameters();
if (
is_array($routeParameter) &&
array_key_exists(EA::ENTITY_FQCN, $routeParameter) &&
$routeParameter[EA::ENTITY_FQCN] == $entity
) {
return $id;
}
}
return null;
}
This code works for me. But this code only works within the DashboardController. If i want to create Admin URLs within a CRUD controller i need to move the menu config to a static method and accessing it there.
Also i am not fetching the error case when i cannot determine the menuIndex and returning null. For my case it's fine.
Maybe this is helpfull for someone.
If somebody has better solution, i would be happy to here about it.
I'm new in Symfony and i have a little problem.
I been searching online, couldn't find the answer to my problem.
I create API and I have two controllers(UserController, InstructorController) with similar method (addUserImage, addInstructorImage). I created abstract BaseImage(Here is saving files), InstructorImage and UserImage(here are set path). This is my controller:
/**
* #ApiDoc(
* name="addInstructorImage", section="Instructors",
* description="Add Instructor Image",
* )
*
* #Route("/instructor/{instructor}/image", name="instructors.image.add")
* #Method("POST")
*
* #param Request $request
* #param Instructor $instructor
* #View
*
* #return \FOS\RestBundle\View\View
*/
public function addInstructorImage(Request $request, Instructor $instructor)
{
$this->denyAccessUnlessGranted('edit', $instructor->getUser());
$image = new InstructorImage();
$form = $this->get('form.factory')
->createNamed('', InstructorImageType::class, $image, ['csrf_protection' => false, 'method' => 'POST']);
$form->handleRequest($request);
if ($form->isValid()) {
$image->setInstructor($instructor);
$em = $this->getDoctrine()->getManager();
$em->persist($image);
$em->flush();
return $this->view(null, Response::HTTP_NO_CONTENT);
}
return $this->view($form, Response::HTTP_BAD_REQUEST);
}
My second controller is identical.
The only difference is another object , and another form. What is the best way to optimize this code. Should I create services that adds photos or use chain handler? Maybe You have better ideas?
Thank you for your help
Short answer: Don't waste time on trying to optimize this sort of stuff.
Your method consist of 19 lines of code. It's all basic boiler plate easy to read code. I know whenever I see duplicate code I'm tempted to try and combine it somehow but what exactly would be gaining? Reducing 19 lines to maybe 15?
If I was going to change your code then I'd be tempted to move some of the business logic into it's own service. Something like:
InstructorManager::addImage($instructor,$image);
That would get rid of the entity manager boiler plate and provide a bit of abstraction. Might make it a bit easier to test though all the method is doing is setting the image and calling flush. Hardly worth the effort. Might be worth it if you have other manager type functionality to add as well. Or maybe you want to be able to add images from a console app.
Of course you may end needing to add functionality in the future. Maybe you want to notify someone when the image is changed. If you find yourself needing to modify duplicated code then you can probably justify the effort of moving common code into it's own service.
And I suppose you could create a service for your form using the container's factory capability. Especially if you had a bunch of these form to make. But again, hardly worth the effort and might even make things more difficult to maintain.
I'm currently researching Symfony CMF and PHPCR for a project I recently started. What I'm currently trying to figure out is how to create a Route and save it into the database. As far as I understand, I must use Symfony\Cmf\Bundle\RoutingBundle\Doctrine\Phpcr\Route and persist the element into the database. This works fine, but automatically generates a route path, which is not what I want. What I need to do is generate a custom route which links to a specific controller. Here is my code:
$em = $this->get('doctrine_phpcr.odm.document_manager');
$parent = $em->find(null, '/cms/routes');
$route = new \Symfony\Cmf\Bundle\RoutingBundle\Doctrine\Phpcr\Route();
$route->setParentDocument($parent);
$route->setName('my_route_name');
$route->setDefault('_controller', 'AppBaseBundle:Frontend/Users:index');
$em->persist($route);
$em->flush();
If i execute this code, the generated route will be /cms/routes/my_route_name. From what I can see, you could use $route->setPath('/testing');, but that generates the following exception:
Can not determine the prefix. Either this is a new, unpersisted document or the listener that calls setPrefix is not set up correctly.
Does anybody have any ideas how to solve this?
In PHPCR, every document has a path where it is store. If you are familiar with doctrine ORM, the path has the role of the ID. The difference with ORM is that all documents (regardless of their type) live in the same tree. This is great, because your route can reference just anything, it is not limited to specific document types. But we need to create some structure with the paths. This is why we have the prefix concept. All routes are placed under a prefix (/cms/routes by default). That part of the document path is removed for the URL path. So repository path /cms/route/testing is the url domain.com/testing.
About your sample code: Usually, you want to configure the controller either by class of the content document or by route "type" attribute to avoid storing a controller name into your database to allow for future refactoring. A lot of this is explained in the [routing chapter of the CMF documentation][1] but the prefix is only used there, not explicitly explained. We need to improve the documentation there.
[1] http://symfony.com/doc/master/cmf/book/routing.html
I managed to find a way to overcome this issue. Because in my project I also have the RouteAutoBundle, I created a class which extends \Symfony\Cmf\Bundle\RoutingBundle\Doctrine\Phpcr\Route. Inside that class I added:
/**
* #PHPCR\Document(referenceable=true)
*/
class MenuRoute extends Route
{
protected $url;
/**
* Get $this->url
*
* #return mixed
*/
public function getUrl() {
return $this->url;
}
/**
* Set $this->url
*
* #param mixed $url
*/
public function setUrl($url) {
$this->url = $url;
}
}
After that I added this to cmf_routing_auto.yml:
App\MenuBundle\Document\MenuRoute:
uri_schema: /{getUrl}
token_providers:
getUrl: [content_method, { method: getUrl }]
So now one would just create an instance of MenuRoute (just like when using Route) and call the method setUrl($your_url) passing the desired url.
If anybody finds a better way, I'm opened to suggestions.
I'm trying to generate link to URL which contains two parameters (both of those parameters are not really necessary but I do it for practice). I created custom showAction in DiscovererController
/**
* #Route("/rivers/{river_id}/discoverers/{id}", name="discoverer_show")
* #Template
*/
public function showAction($river_id, $id){
$em = $this->getDoctrine()->getEntityManager();
$river = $em->getRepository('MyOwnBundle:River')->find($river_id);
if(!$river){
throw $this->createNotFoundException("no river with provided id");
}
$entity = $river->getDiscoverer();
return array('entity' => $entity);
}
As you can see two parameters are passed, id of the river and id of the discoverer (which is absurd but as I said, practice...).
In show action of a river (/rivers/1) I decided to put following code:
<p>{{entity.discoverer.name}}</p>
Note that 'entity' is a river here, and river has a discoverer. Unfortunatelly, when I try to render this action, I get error which tells me that:
An exception has been thrown during the rendering of a template ("Unable to generate a URL for the named route "discoverer_show" as such route does not exist.") in /path/to/project/src/My/OwnBundle/Resources/views/River/show.html.twig at line 9.
I dont have a clue what is wrong, I provided both necessary parameters and used "discoverer_show" which I defined in my controller. How to correctly render this link?
A piece of advice: do not use tabs in your source code at all! Make your IDE to replace tab character with 4 spaces. This could save you a lot of trouble... Tabs does not behave well in git too.
Ok, by accident i figured it out. Turns out annotations in symfony2 CANNOT begin with tab.
So this thing right here is NOT going to work
/**
* #Route("/people")
*/
But this will work like a charm:
/**
* #Route("/people")
*/