Bit of an obscure one this. I'm reading that Symfony get's muddled when dealing with more than one route of a similar pattern. Here goes, this is what I've tried thus far:-
For starters, I hit the endpoint: https://127.0.0.1:8000/api/contracts/12345/new which returns the 404 error in full:-
{type: "https://tools.ietf.org/html/rfc2616#section-10", title: "An error occurred", status: 404,…}
class
:
"Symfony\\Component\\HttpKernel\\Exception\\NotFoundHttpException"
detail
:
"App\\Entity\\Project object not found by the #ParamConverter annotation."
status
:
404
title
:
"An error occurred"
trace
:
[{namespace: "", short_class: "", class: "", type: "", function: "",…},…]
type
:
"https://tools.ietf.org/html/rfc2616#section-10"
Here's a snapshot of my URL patterns:-
docker-compose exec app bin/console debug:router
new_contract POST ANY ANY /api/contracts/{id}/new
api_edit_project POST ANY ANY /api/contracts/{id}/edit
They're very similar but I'm using the new endpoint from above. Here's the controller:-
/**
* #Route("/api")
*/
class ContractController extends BaseApiController
{
/**
* #Post ("/contracts/{id}/new", name="new_contract")
*/
public function postNewContractAction(){
// -- we don't hit this method at all
}
/**
* #Post ("/contracts/{id}/edit", name="api_edit_project")
*/
public function postEditContractAction(){}
}
Further to this, I've tried moving the controller methods around in terms of ordering, but this has no effect.
Any ideas?
As statet in the Symfony Documentation, receiving a 404 Error, when trying to fetch an object by it's id automatically using the paramConverter magic, this usually means there is no data for that id.
If no Post object is found, a 404 Response is generated;
I suspect there is no Project with id=12345.
Why are you asking for an {id} in the /new route, actually? To my understanding you would not have an ID in that case, yet.
I always try to set the parameters at last position in routes, as it may avoid route collisions.
Related
My sonata admin project should be accessable through {slug} url parameter:
/{slug}/admin/user/list
My routing configuration:
_sonata_admin:
resource: '.'
type: 'sonata_admin'
prefix: '/{slug}/admin'
Now when I access the admin I see this error:
An exception has been thrown during the rendering of a template ("Some
mandatory parameters are missing ("slug") to generate a URL for route
"admin_app_user_list".").
I tried configuring persistent parameters from my admin like this:
/**
* {#inheritdoc}
*/
protected function configurePersistentParameters(): array
{
return ['slug' => $this->getUser()->getUsername()];
}
But I still got the same error.
Upon checking when configurePersistentParameters is being called, in DefaultRouteGenerator::generateMenuUrl() I noticed that admin should have request object assigned:
if ($admin->hasRequest()) {
$parameters = array_merge($admin->getPersistentParameters(), $parameters);
}
If I remove the condition, the error is not displayed. So I guess when generating menu urls, the request object is not yet set.
Is there a way of passing route parameters when generating menu urls?
I've used the thephpleague/tactician-bundle with Symfony before, but this is the first time I've used it with Symfony 4.* (specifically 4.1.4) and attempted to use a single handler Class for my Application Service.
When I execute a command in the Controller
public function postAction(Request $request, CommandBus $commandBus)
{
$form = $this->createForm(VenueType::class);
$form->submit($request->request->all(), true);
$data = $form->getData();
if($form->isValid()) {
$command = new CreateVenueCommand($data);
$commandBus->handle($command);
return $form->getData();
}
return $form;
}
... I get the following error:
"error": {
"code": 500,
"message": "Internal Server Error",
"exception": [
{
"message": "Could not invoke handler for command App\\Application\\Command\\CreateVenueCommand for reason: Method 'handle' does not exist on handler",
"class": "League\\Tactician\\Exception\\CanNotInvokeHandlerException",
"trace": [
I've seemingly followed the installation documents for the tactician-bundle and installed it using Flex. As far as I can tell everything is configured correctly, so I'm unsure what I'm missing in my implementation.
Implementation
As per the thephpleague/tactician-bundle installation guide I've installed using Flex and the bundle is registered and the config package installed:
tactician:
commandbus:
default:
middleware:
- tactician.middleware.locking
- tactician.middleware.doctrine
- tactician.middleware.command_handler
After creating the DTO Command Class 'CreateVenueCommand', I created the handler Class:
use App\Infrastructure\Domain\Model\VenueRepositoryInterface;
use App\Application\Command\CreateVenueCommand;
use App\Domain\Entity\Venue;
class VenueApplicationService
{
private $venueRepository;
public function __construct(VenueRepositoryInterface $venueRepository)
{
$this->venueRepository = $venueRepository;
}
/**
* #param CreateVenueCommand $aCommand
* #throws \Exception
*/
public function createVenue(CreateVenueCommand $aCommand)
{
$aVenue = new Venue($aCommand->getData())
if ($aVenue === null) {
throw new \LogicException('Venue not created');
}
$this->venueRepository->add($aVenue);
}
Then I registered the handler Class as a Service taking advantage of Symfony's autowiring and Tacticians typehints:
App\Application\VenueApplicationService:
arguments:
- '#App\Infrastructure\Persistence\Doctrine\DoctrineVenueRepository'
tags:
- { name: tactician.handler, typehints: true }
So according to the installation documents, typehints work if:
The method must be public.
The method must accept only one parameter.
The parameter must be typehinted with a class name.
Also, and this is specific to my use case:
If you have multiple commands going into a single handler, they will all be detected, provided they follow the rules above. The actual name of the method is NOT important.
So when I invoke the commandbus in the Controller Class, I'm unsure why I'm getting the error above.
If I change the Command Handler method to:
public function handle(CreateVenueCommand $aCommand)
{
... then it works fine. This would seem to suggest that the typehints aren't working as documented.
It seems in this case that the actual name of the method IS important. ... or I've made some form of error in my implementation ... or I'm misunderstanding the multiple commands going into a single handler use case??
Any assistance would be greatly appreciated.
Solution
With a big thanks to kunicmarko20 for pointing me in the right direction.
Specifically for my use case I simply needed to use one of Tacticians MethodNameInflector classes, configured in Symfony thus:
tactician:
commandbus:
default:
middleware:
- tactician.middleware.locking
- tactician.middleware.doctrine
- tactician.middleware.command_handler
method_inflector: tactician.handler.method_name_inflector.handle_class_name
... then it was simply a matter of naming each Handler method in my Application Service class 'handle{whateverYouLike}Command
Here under 1. is explained how the naming works, if you want to use a different name than in this table you can implement MethodNameInflector Interface and provide a name of the method.
I'm trying to start sending my logs into elastic search using monolog. (I'm using Symfony2).
I've set up monolog like this:
monolog:
handlers:
elasticsearch:
elasticsearch:
host: %logger_elastic_host%
port: %logger_elastic_port%
type: elasticsearch
level: info
It worked only few minutes until it broke with this error messages(a fatal error, I removed useless stuff):
create: /monolog/logs/AVQKYsGRPmEhlo7mDfrN caused
MapperParsingException[failed to parse [context.stack.args]]; nested:
ElasticsearchIllegalArgumentException[unknown property [class]];
I've been looking with my collegue how to fix that. What we found out is:
Elastic search receive the first logs and automatically build a mapping
We send new logs with another mapping or slightly different to what was sent before and it breaks.
In this case it's breaking here: context.stack.args.
The problem is that the context will always be very different.
What we would like is:
is anyone out there using Monolog to log to Elasticsearch
How do you guys manage to avoid this issue. (How can we manage to avoid it)?
thanks guys.
This is happening because ES creates a mapping from the first document. If any document that is inserted after has the same property but with other type/format then ES will throw an error.
A solution is to create a custom monolog formatter and register it:
config.yml:
elasticsearch:
type: elasticsearch
elasticsearch:
host: elasticsearch
ignore_error: true
formatter: my_elasticsearch_formatter
This line will make Monolog\Handler\ElasticSearchHandler ignore any other errors from Ruflin's Elastica package:
ignore_error: true
Then register a service with this name: my_elasticsearch_formatter:
<service id="my_elasticsearch_formatter" class="AppBundle\Services\MyFormatter">
<argument type="string">monolog</argument>
<argument type="string">logs</argument>
</service>
first argument is the index name, second arg is the type.
And the formatter class:
<?php
namespace AppBundle\Services;
use function json_encode;
use Monolog\Formatter\ElasticaFormatter;
use function var_dump;
class MyFormatter extends ElasticaFormatter
{
/**
* #param string $index
* #param string $type
*/
public function __construct($index, $type)
{
parent::__construct($index, $type);
}
/**
* #param array $record
* #return array|\Elastica\Document|mixed|string
*/
public function format(array $record)
{
$record['context'] = json_encode($record['context']);
return parent::format($record);
}
}
The downside of this solution is that it will json_encode the context. You will not be able to filter by inner properties of the context in ES but at least you will not lose important information about your logs.
I'm trying to get started with Symfony2 and I'm getting stuck on the "create your first page" tutorial : http://symfony.com/doc/current/book/page_creation.html
I created the controller file (copy-pasted from tutorial) as E:\Web\project\src\AppBundle\Controller\LuckyController.php :
<?php
// src/AppBundle/Controller/LuckyController.php
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\HttpFoundation\Response;
class LuckyController
{
/**
* #Route("/lucky/number")
*/
public function numberAction()
{
$number = rand(0, 100);
return new Response(
'<html><body>Lucky number: '.$number.'</body></html>'
);
}
}
I didn't make any change to any other file.
When I go to the URL 127.0.0.1/project/web/app_dev.php/lucky/controller (127.0.0.1 is the virtual host for E:\Web) I keep getting the error "No route found for "GET /lucky/controller"".
404 Not Found - NotFoundHttpException
1 linked Exception:
[2/2] NotFoundHttpException: No route found for "GET /lucky/controller/"
[1/2] ResourceNotFoundException:
If I remove the "/web" from the URL, 127.0.0.1/project/app_dev.php/lucky/controller, I get a 404 error.
I'm pretty sure the error comes from the folder structure since the files are unmodified. Similar issue was reported here but none of these suggestions solved my error.
Just figured out that the URL should be 127.0.0.1/project/web/app_dev.php/lucky/number (and not controller). Problem solved.
Be careful, your route is /lucky/number not /lucky/controller.
So, you have to try with 127.0.0.1/project/web/app_dev.php/lucky/number
I have created a custom Error 403 page using the access_denied_url parameter in security.yml: when showing the new page I want to tell people that the problem happened when trying to access page xxxxx.
$request->headers->get('referer') is empty.
/**
* #Route("/forbidden", name="error_403")
* #Template()
*/
public function error403Action(Request $request)
{
return array('referer' => $request->headers->get('referer'));
}
How can I get information about the original Request (the one that resulted in this forward)?
It got solved calling {{ app.request.server.get('PHP_SELF') }} directly in the Twig template.