Sylius : Catchable Fatal Error: Argument 1 passed to AppBundle\Controller\ProductController::__construct() - sylius

I'm trying to override the ProductController,i've followed this docs :
http://docs.sylius.org/en/stable/bundles/general/overriding_controllers.html
but an exception keep appearing:
Catchable Fatal Error: Argument 1 passed to Sylius\Bundle\ResourceBundle\Controller\ResourceController::__construct() must implement interface Sylius\Component\Resource\Metadata\MetadataInterface, none given, called in C:\wamp3\www\sidratsoft_website\app\cache\dev\appDevDebugProjectContainer.php on line 1382 and defined
AppBundle/Controller/ProductController.php:
<?php
namespace AppBundle\Controller;
use FOS\RestBundle\View\View;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Sylius\Component\Resource\Metadata\MetadataInterface;
use Sylius\Bundle\ResourceBundle\Controller\ResourceController;
use Sylius\Component\Resource\ResourceActions;
class ProductController extends ResourceController
{
public function allProdSpecificTaxonAction(Request $request,$t){
$locale = $this->get('sylius.context.locale')->getLocale();
/** #var Taxon $taxon */
$taxon = $this->get('sylius.repository.taxon')
->findByName('Honeywell',$locale);
var_dump($taxon);
exit;
}
}
?>
app/config/routing.yml:
app_bundle_product:
path: /products/cat/{taxon}
defaults:
_controller: AppBundle:Product:allProdSpecificTaxonAction
app/config/config.yml:
sylius_product:
resources:
product:
classes:
controller: AppBundle\Controller\ProductController

I suppose you don't have to register controller as service in AppBundle/Resoures/config/services.yml. As you extend ResourceController (which is totally correct) it has to have many dependency injected - fortunately , you don't have to inject them yourself, as Sylius will do the job and automatically register controller with dependencies injected.

i thiink i fixed it, the problem was in the _controller attribute in the routing :
from this :
app_bundle_product:
path: /products/cat/{taxon}
defaults:
_controller: AppBundle:Product:allProdSpecificTaxonAction
to:
app_bundle_product:
path: /products/cat/{taxon}
defaults:
_controller: sylius.controller.product:allProdSpecificTaxonAction

Related

How to run Codeception functional tests against a custom frameworkless web application? (that implements PSR-15 RequestHandlerInterface)

Assume the following simple web app:
<?php
// src/App/App.php
namespace Practice\Sources\App;
use Closure;
use Laminas\Diactoros\ServerRequestFactory;
use Laminas\HttpHandlerRunner\Emitter\SapiEmitter;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Server\RequestHandlerInterface;
class App implements RequestHandlerInterface
{
private Closure $requestProvider;
private ResponseFactoryInterface $responseFactory;
private SapiEmitter $responseEmitter;
public function __construct(ResponseFactoryInterface $responseFactory)
{
$this->requestProvider = fn() => ServerRequestFactory::fromGlobals();
$this->responseFactory = $responseFactory;
$this->responseEmitter = new SapiEmitter();
}
public function run(): void
{
$request = ($this->requestProvider)();
$response = $this->handle($request);
$this->responseEmitter->emit($response);
}
public function handle(RequestInterface $request): ResponseInterface
{
$response = $this->responseFactory->createResponse();
$response->getBody()->write('hello world');
return $response;
}
}
One can easily run it by placing the following code in their web front-controller (e.g. public_html/index.php):
<?php
// web-root/front-controller.php
use Laminas\Diactoros\ResponseFactory;
use Practice\Sources\App\App;
require_once __DIR__.'/../vendor/autoload.php';
$responseFactory = new ResponseFactory();
$app = new App($responseFactory);
$app->run();
Now, I'd like to run Codeception feature tests against it, written in Gherkin. Consider the following simple test:
# tests/Feature/run.feature
# (also symlink'ed from tests/Codeception/tests/acceptance/ )
Feature: run app
Scenario: I run the app
When I am on page '/'
Then I see 'hello world'
To run acceptance tests against it, I have to provide my steps implementation. I'll reuse standard steps for that, provided by the Codeception:
<?php
// tests/Codeception/tests/_support/FeatureTester.php
namespace Practice\Tests\Codeception;
use Codeception\Actor;
abstract class FeatureTester extends Actor
{
// use _generated\AcceptanceTesterActions;
// use _generated\FunctionalTesterActions;
/**
* #When /^I am on page \'([^\']*)\'$/
*/
public function iAmOnPage($page)
{
$this->amOnPage($page);
}
/**
* #Then /^I see \'([^\']*)\'$/
*/
public function iSee($what)
{
$this->see($what);
}
}
<?php
// tests/Codeception/tests/_support/AcceptanceTester.php
namespace Practice\Tests\Codeception;
class AcceptanceTester extends FeatureTester
{
use _generated\AcceptanceTesterActions;
}
# tests/Codeception/codeception.yml
namespace: Practice\Tests\Codeception
paths:
tests: tests
output: tests/_output
data: tests/_data
support: tests/_support
envs: tests/_envs
actor_suffix: Tester
extensions:
enabled:
- Codeception\Extension\RunFailed
# tests/Codeception/tests/acceptance.suite.yml
actor: AcceptanceTester
modules:
enabled:
- PhpBrowser:
url: http://practice.local
gherkin:
contexts:
default:
- \Practice\Tests\Codeception\AcceptanceTester
But now, I want to use same tests code to run these same tests as functional tests using Codeception. For this, I have to enable the module that implements these same steps in functional way. Which one do I use? Codeception provides several, but they're for 3rd party frameworks, e.g. Laravel, Yii2, Symphony etc. What do I do for such a simple app that doesn't use any 3rd party framework?
Here's what I've managed to do. I've created my own \Codeception\Lib\InnerBrowser implementation that inherits from \Codeception\Module\PhpBrowser provided by Codeception, in which I substitute the web client that Codeception uses (it uses Guzzle) with my own implementation (which also inherits from the Guzzle client) that doesn't perform any web requests but requests my app instead:
# tests/Codeception/tests/functional.suite.yml
actor: FunctionalTester
modules:
enabled:
- \Practice\Tests\Codeception\Helper\CustomInnerBrowser:
url: http://practice.local
gherkin:
contexts:
default:
- \Practice\Tests\Codeception\FunctionalTester
<?php
// tests/Codeception/tests/_support/FunctionalTester.php
namespace Practice\Tests\Codeception;
class FunctionalTester extends FeatureTester
{
use _generated\FunctionalTesterActions;
}
In order for this to work, I have to make my app return Guzzle Responses (which implement PSR's ResponseInterface as well) - because PhpBrowser expects its web client to return them - which is why I had to make the ResponseFactory a constructor parameter to be able to substitute it in tests.
<?php
// tests/Codeception/tests/_support/Helper/CustomInnerBrowser.php
namespace Practice\Tests\Codeception\Helper;
use Codeception\Module\PhpBrowser;
use Http\Factory\Guzzle\ResponseFactory;
use Practice\Sources\App\App;
class CustomInnerBrowser extends PhpBrowser
{
private App $app;
public function __construct(...$args)
{
parent::__construct(...$args);
$responseFactory = new ResponseFactory();
$this->app = new App($responseFactory);
}
public function _prepareSession(): void
{
parent::_prepareSession();
$this->guzzle = new CustomInnerBrowserClient($this->guzzle->getConfig(), $this->app);
$this->client->setClient($this->guzzle);
}
}
<?php
// tests/Codeception/tests/_support/Helper/CustomInnerBrowserClient.php
namespace Practice\Tests\Codeception\Helper;
use GuzzleHttp\Client as GuzzleClient;
use Practice\Sources\App\App;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
class CustomInnerBrowserClient extends GuzzleClient
{
private App $app;
public function __construct(array $config, App $app)
{
parent::__construct($config);
$this->app = $app;
}
public function send(RequestInterface $request, array $options = []): ResponseInterface
{
return $this->app->handle($request);
}
}
In such a configuration, everything seems to work fine.
But there's a problem. Notice the App::handle() signature:
public function handle(RequestInterface $request): ResponseInterface
- it differs from the one that it implements, which is declared in RequestHandlerInterface:
public function handle(ServerRequestInterface $request): ResponseInterface;
Technically, it's completely legal because it doesn't break the parameter contravariance required by the Liskov Substitution Principle.
The problem that I've faced is that PhpBrowser assumes that it sends a (client-side) RequestInterfaces (over the network) but my app requires a (server-side) ServerRequestInterface instead, to be able to access parameters that are set on the server side such as ServerRequestInterface::getParsedBody(), session etc.
How do I workaround this? Framework modules provided by Codeception already do this somehow... And BTW, haven't Codeception (or someone else) provided yet an easy way to run functional tests against custom code?
Here's composer.json BTW:
{
"require": {
"php": "~7.4",
"laminas/laminas-diactoros": "^2.5",
"laminas/laminas-httphandlerrunner": "^1.3"
},
"require-dev": {
"codeception/codeception": "^4.1",
"codeception/module-phpbrowser": "^1.0.0",
"http-interop/http-factory-guzzle": "^1.0"
},
"autoload": {
"psr-4": {
"Practice\\Sources\\": "src"
}
},
"autoload-dev": {
"psr-4": {
"Practice\\Tests\\Unit\\": "tests/Unit/",
"Practice\\Tests\\Support\\": "tests/Support/",
"Practice\\Tests\\Codeception\\": "tests/Codeception/tests/_support/",
"Practice\\Tests\\Codeception\\_generated\\": "tests/Codeception/tests/_support/_generated/",
"Practice\\Tests\\Codeception\\Helper\\": "tests/Codeception/tests/_support/Helper/"
}
},
"scripts": {
"test-feature": "codecept run --config tests/Codeception/codeception.yml"
}
}

ASP Core: how to route to API Controller that is located in the area folder?

The structure:
+ MyProj
+ Areas
+ Configuration
- Pages
- ConfigurationApiController.cs
To create controller without Controllers folder was proposed by VS2017 and it is ok for me since I use Razor Pages and do not need Controllers folder:
Those doesn't work:
http://localhost:8080/api/Users
http://localhost:8080/api/GetUsers
http://localhost:8080/Configuration/api/Users
http://localhost:8080/Configuration/api/GetUsers
Controller defined:
[Route("api")]
[Produces("application/json")]
[ApiController]
public class ConfigurationApiController : ControllerBase
{
private readonly ApplicationSettings applicationSettings;
[HttpGet]
public ActionResult GetUsers()
{
Mvc routing configured standard way:
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
How to route to GetUsers action of ConfigurationApiController ?
Modify the api route and add the Area Attribute to provide the area name for [area] route.
[Area("Configuration")]
[Route("[area]/api/[controller]")]
[ApiController]
public class ConfigurationApiController : ControllerBase
{
}
That's all, and it can be accessed at http://localhost:8080/Configuration/api/ConfigurationApi
Some other routing options:
Using AspNetCore.RouteAnalyzer working option found: http://localhost:8080/api (yes, without action)
After removing web APIs attributes
// [Route("api")]
// [Produces("application/json")]
// [ApiController]
then http://localhost:8080/ConfigurationApi/GetUsers
it could be ok but there is no area in the routing and it seems "routing to the area by conventions" doesn't work is asp core:
ASP Core: how to configure area for api controller without AreaAttribute (or how to enable convention area routing for Api controller)?
and https://github.com/aspnet/AspNetCore/issues/7042
Also in this case ContentResult { Content = json, ContentType = "application/json" } should be return but this is ok for me since I prefer to use in place serialization instead of stream serializers.
This routes to http://localhost:8080/Configuration/api
[Area("Configuration")]
[Route("[area]/api")]
[Produces("application/json")]
[ApiController]
other option: [Route("[area]/api/[action]")] routes to http://localhost:8080/Configuration/api/GetUsers
when removing area attribute throws the run-time error Error: While processing template '[area]/api', a replacement value for the token 'area' could not be found. Available tokens: 'action, controller'. To use a '[' or ']' as a literal string in a route or within a constraint, use '[[' or ']]' instead.
//[Area("Configuration")]
[Route("[area]/api")]
[Produces("application/json")]
[ApiController]
To support #Url.Action(action: "myArea", controller: "myControllerApi") routing should be configured manually.
Asp Core routes:
app.UseMvc(routes =>
{
routes.MapRoute(
name: "defaultArea",
template: "{area:exists}/{controller}/{action}"); // matches only those where area route value is defined
});
Asp Core 3 routes (startup Configure):
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapControllerRoute(
name: "defaultArea",
pattern: "{area:exists}/{controller}/{action}");
});

Issue running Symfony 3 on Apache

I have Wamp64 running on my Windows 10 machine.
Localhost set up fine and I can see the Wamp64 home page at Localhost.
I've installed Symfony and followed the instructions as per their website.
I set up "project1" at C:\wamp64\www\project1, it has a public directory with an index.php file in it.
When I browse to http://localhost/project1/public/index.php I get an HTTP 404 error
Error page...
Should be getting the Symfony welcome page.
Any help gratefully received
You need to define a route with the path /
routes.yaml:
home:
path: /
defaults: { _controller: 'AppBundle\Controller\DefaultController::index' }
OR
Annotation:
// src/AppBundle/Controller/DefaultController.php
namespace AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Routing\Annotation\Route;
class DefaultController extends Controller
{
/**
* Matches / exactly
*
* #Route("/", name="home")
*/
public function index()
{
// ...
}
}
You can also define these with XML and PHP. See the docs.
IF you're using Symfony 4 as suggested in the comments, the namespace is App\Controller and the localtion of your controller is /src/Controller instead of /src/AppBundle/Controller

Codeception 2.2 api tests ZF2 and PhpBrowser module together

Codeception API tester require PhpBrowser module and I want to use ZF2 module because I need retrieve some services from ServiceManager.
After update Codeception to 2.2 it throws this exception:
[Codeception\Exception\ModuleConflictException] ZF2 module conflicts
with PhpBrowser
Is there any way to enable ZF2 and PhpBrowser together in Codeception 2.2?
If you have a good reason to use ZF2 in the same suite as PhpBrowser,
you can create your own helper class and load ZF2 module as a dependency.
Configuration:
modules:
enabled:
- PhpBrowser:
url: http://localhost/
- \Helper\Zf2Helper:
depends: ZF2
Code tests/_support/Helper/Zf2Helper.php:
<?php
namespace Helper;
class Zf2Helper extends \Codeception\Module
{
private $zf2;
public function _inject(\Codeception\Module\ZF2 $zf2)
{
$this->zf2 = $zf2;
}
public function doSomethingWithZf2
{
$this->zf2->doSomething();
}
}
Update:
Since Codeception 2.2.2 release it is possible to load services part of ZF2 which enables grabServiceFromContainer method.
Configuration:
modules:
enabled:
- PhpBrowser:
url: http://localhost/
- ZF2
part: services
Thank your for your answer.
Working code with some improvements:
<?php
namespace Helper;
use Codeception\Lib\Interfaces\DependsOnModule;
class Zf2Helper extends \Codeception\Module implements DependsOnModule
{
protected $dependencyMessage = <<<EOF
Example configuring ZF2Helper as proxy for ZF2 module method grabServiceFromContainer.
--
modules:
enabled:
- \Helper\ZF2Helper:
depends: ZF2
--
EOF;
private $zf2;
public function _inject(\Codeception\Module\ZF2 $zf2)
{
$this->zf2 = $zf2;
}
public function _depends()
{
return ['Codeception\Lib\ZF2' => $this->dependencyMessage];
}
public function grabServiceFromContainer($service)
{
return $this->zf2->grabServiceFromContainer($service);
}
}

Symfony3 - Extend namespace

I have the following directory structure in my symfony3 structure:
src
AppBundle
AppBundle.php
Entity
User
User.php
UserRepository.php
On top of my User.php:
<?php
namespace AppBundle\Entity\User;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* #ORM\Table(name="user")
* #ORM\Entity(repositoryClass="AppBundle\Entity\User\UserRepository")
*/
class User implements UserInterface {
and it tells me:
undefined constant User
Is there any way extending the namespace?
Thanks and Greetings!
EDIT:
occurrence of "user":
security:
providers:
in_memory:
memory:
users:
admin:
password: admin
roles: 'ROLE_ADMIN'
our_db_provider:
entity:
class: AppBundle:User
property: username
encoders:
AppBundle\Entity\User\User:
algorithm: bcrypt
AppBundle:User alias with your configuration resolves to AppBundle\Entity\User class, which obviously doesn't exist. Either use the full namespace AppBundle\Entity\User\User or the AppBundle:User\User alias.
This is how auto-mapping works. It looks for classes in the Entity folder in each bundle.
See the doctrine reference docs for more.