I Want to extend Symfony2 Controller to my project that is using API but I am having error of a non object use getParameter() function look at my code:
namespace Moda\CategoryBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class ApiController extends Controller
{
/**
* #var String
*/
protected $_host;
/**
* #var String
*/
protected $_user;
/**
* #var String
*/
protected $_password;
public function __construct()
{
$this->_host = $this->container->getParameter('api_host');
$this->_user = $this->container->getParameter('api_user');
$this->_password = $this->container->getParameter('api_password');
}
}
And next Controller
namespace Moda\CategoryBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
class CategoryController extends ApiController
{
/**
* #Route("/category", name="_category")
* #Template()
*/
public function indexAction()
{
return array('name' => 'test');
}
}
And the end, I got this Fatal Error:
FatalErrorException: Error: Call to a member function getParameter()
on a non-object in (..)
I try to use $this->setContainer() but it doesn't work. Do you have any idea how can I slove this problem?
If your controller is not defined as service, The constructor execution of the controller is not persisted.
You have two options to solve your situation:
Define the controller as a service and inject the parameters you need using dependency injection.
Add an init method in the controller, or on a parent abstract controller, and call the init method, before the action you need to have these parameters available;
You cant use container in Controller __construct at reason that when constructor called where is none container set yeat.
You can simply define some simple methods in controller like
class ApiController extends Controller
{
protected function getApiHost()
{
return $this->container->getParameter('api_host');
}
}
I wonder if something crazy like this would work? Instead of overriding the constructor, override the setContainer method? I haven't tried it...just thinking out loud.
namespace Moda\CategoryBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\DependencyInjection\ContainerInterface;
class ApiController extends Controller
{
/**
* #var String
*/
protected $_host;
/**
* #var String
*/
protected $_user;
/**
* #var String
*/
protected $_password;
public function setContainer(ContainerInterface $container = null)
{
parent::setContainer($container);
$this->_host = $this->container->getParameter('api_host');
$this->_user = $this->container->getParameter('api_user');
$this->_password = $this->container->getParameter('api_password');
}
}
Related
I cannot find the problem using
$routes->resource
Please help me figure out what is the problem.
This is how I put my routes resource in config routes :
$routes->resource('ApiManageBanner', ['controller' =>'App\Controllers\ApiData\ApiManageBanner']); // get, put, create, delete
Recently I just move all my project to the newest codeigniter 4 version 4.2.6 from the previous version 4.1.2
This is my controllers :
<?php
namespace App\Controllers\ApiData;
use App\Controllers\BaseController;
use CodeIgniter\RESTful\ResourceController;
use Codeigniter\API\ResponseTrait;
class ApiManageBanner extends ResourceController
{
use ResponseTrait;
function __construct()
{
}
// equal to get
public function index() {
echo "Test";
}
// equal to post
public function create() {
}
// equal to get
public function show($id = null) {
}
// equal to put
public function update($id = null) {
}
// equal to delete
public function delete($id = null) {
}
}
I just try a simple to echo "Test".
But I got this error :
I search everywhere but cannot find the problem related to the error.
If I change the routes name to 'ApiManageBanners' using 's' :
$routes->resource('ApiManageBanners', ['controller' =>'App\Controllers\ApiData\ApiManageBanner']); // get, put, create, delete
It is working.
But I cannot change my routes name because my application is reading
'ApiManageBanner' not 'ApiManageBanners'
I am very curious what cause the problem. It is not working for almost all my resources api controller routes.
I found the problem. According to the error it is related to session. When I check all my file. I found that I always init the :
$this->Session = \Config\Services::session();
In all my controller and model __construct();
function __construct()
{
$this->Session = \Config\Services::session();
}
So I remove it and init globaly in BaseController.php
<?php
namespace App\Controllers;
use CodeIgniter\Controller;
use CodeIgniter\HTTP\CLIRequest;
use CodeIgniter\HTTP\IncomingRequest;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use Psr\Log\LoggerInterface;
/**
* Class BaseController
*
* BaseController provides a convenient place for loading components
* and performing functions that are needed by all your controllers.
* Extend this class in any new controllers:
* class Home extends BaseController
*
* For security be sure to declare any new methods as protected or private.
*/
abstract class BaseController extends Controller
{
/**
* Instance of the main Request object.
*
* #var CLIRequest|IncomingRequest
*/
protected $request;
/**
* An array of helpers to be loaded automatically upon
* class instantiation. These helpers will be available
* to all other controllers that extend BaseController.
*
* #var array
*/
protected $helpers = [''];
/**
* Constructor.
*/
public function initController(RequestInterface $request, ResponseInterface $response, LoggerInterface $logger)
{
// Do Not Edit This Line
parent::initController($request, $response, $logger);
// Preload any models, libraries, etc, here.
// E.g.: $this->session = \Config\Services::session();
$this->session = \Config\Services::session();
$this->language = \Config\Services::language();
$this->language->setLocale($this->session->lang);
}
}
Then the error is gone.
I have the following single responsibility repository class
<?php
namespace App\Launches\Repositories\Customer;
use App\Launches\Models\Customer\Subscription;
use Illuminate\Support\Collection;
final class GetSubscriptionRepository
{
/**
* #param int $productId
* #return Collection
*/
public function byProductId(int $productId): Collection
{
return Subscription::where('product_id', $productId)->get();
}
}
I want to be able to test that method, to make sure the object retrieve is the one I've asked. So I've done the following:
<?php
namespace Tests\Unit\Launches\Repository\Customer;
use App\Launches\Models\Customer\Subscription;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Collection;
use Tests\TestCase;
use App\Launches\Repositories\Customer\GetSubscriptionRepository;
class GetSubscriptionRepositoryTest extends TestCase
{
use RefreshDatabase;
/**
* #var GetSubscriptionRepository
*/
protected $getSubscriptionRepository;
/**
* #var Collection
*/
protected $subscriptionCollection;
public function setUp(): void
{
parent::setUp();
$this->getSubscriptionRepository = new GetSubscriptionRepository();
$this->subscriptionCollection = factory(Subscription::class, 20)->make();
}
/**
* #covers GetSubscriptionRepository::byProductId
*/
public function testSubscriptionIsPickedById()
{
$randomSubscription = $this->subscriptionCollection->random(1)->first()->toArray();
$pickedSubscription = $this->getSubscriptionRepository->byProductId($randomSubscription['id']);
$this->assertEquals($randomSubscription['id'], $pickedSubscription->get('id'));
}
}
It's always returning null on $this->getSubscriptionRepository->byProductId($randomSubscription['id']);
It is obviously expecting stuff to be in the database, is there a way to test it without adding stuff to the database?
I have a repository class called EmailRepository
class EmailRepository extends EntityRepository implements ContainerAwareInterface { ... }
I need to get a parameter injected into this repository class but I dont know how...
This is what I currently have inside of the repository, which is being called from my controller:
Controller:
$em->getRepository(Email::class)->getEmailApi();
Repository
class EmailRepository extends EntityRepository implements ContainerAwareInterface {
protected $container;
public function setContainer(ContainerInterface $container = null) {
$this->container = $container;
}
/**
* #param $array
*/
public function getEmailApi($array)
{
echo $this->container->getParameter('email_api');
}
}
I always get this error:
Call to a member function getParameter() on null
The parameter is not null, it does have a value. I know it's telling me that $this->container is null. How do I fix this?
If I run this inside of my controller, it works fine and returns Google
echo $this->getParameter('email_api');
Inject container not a good idea. Try this
services.yaml
App\Repository\EmailRepository:
arguments:
$emailApi: '%env(EMAIL_API)%'
Repository
class EmailRepository
{
protected $emailApi;
public function __construct(string $emailApi)
{
$this->emailApi = $emailApi;
}
/**
* #param $array
*/
public function getEmailApi($array)
{
return $this->emailApi;
}
}
Or via setter injection
services.yaml
App\Repository\EmailRepository:
calls:
- method: setEmailApi
arguments:
$emailApi: '%env(EMAIL_API)%'
Repository
class EmailRepository extends EntityRepository implements ContainerAwareInterface
{
protected $emailApi;
public function setEmailApi(string $emailApi)
{
$this->emailApi = $emailApi;
}
/**
* #param $array
*/
public function getEmailApi($array)
{
return $this->emailApi;
}
}
Your original code is not going to work because there is nothing calling EmailRepository::setContainer. Furthermore, using ContainerAware and injecting the full container is discouraged.
Fortunately, the Doctrine bundle has a new base repository class that the entity manager can use to pull the repository from container and allow you to inject additional dependencies as needed. Something like:
namespace App\Repository;
use App\Entity\Email;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Symfony\Bridge\Doctrine\RegistryInterface;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
class EmailRepository extends ServiceEntityRepository // Different class to extend from
{
private $emailApi;
public function __construct(RegistryInterface $registry, ParameterBagInterface $parameterBag)
{
parent::__construct($registry, Email::class);
$this->emailApi = $parameterBag->get('email_api');
}
So in this case we inject all the parameters and then store the ones we need.
Even injecting the parameter bag is a bit frowned upon. Better to inject individual parameters though this takes just a bit more configuration as we need to use services.yaml to explicitly inject the needed parameters:
public function __construct(RegistryInterface $registry, string $emailApi)
{
parent::__construct($registry, Email::class);
$this->emailApi = $emailApi;
}
#services.yaml
App\Repository\EmailRepository:
$emailApi: 'email_api_value'
I wrote a Command Controller that handles data import from an URL.
pseudo-syntax is like this:
class ImportCommandController extends \TYPO3\CMS\Extbase\Mvc\Controller\CommandController
{
public function importCommand($auth){
$data = file_get_content();
}
}
this works.
But when I try to call that command from the Action Controller of my backend Module I get errors.
Heres the code:
ActionController:
class ImportController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionController
{
/**
* #var \Vendor\MyExt\Command\ImportCommandController importCommandCtrl
* #inject
*/
protected $importCommandCtrl;
public function importAction()//($url,$usr,$pass)
{
//$this->importCommandCtrl = GeneralUtility::makeInstance('Vendor\MyExt\Command\ImportCommandController');
$this->importCommandCtrl->testCommand();
}
}
When I call importAction() like this, I get:
"Call to a member function testCommand() on null"
When I uncomment the makeInstance, I get:
"Call to a member function get() on null"
Sadly, this topic is documente rather poorly in the TYPO3 Docs.
Can someone help me on this or point me to the right direction?
I'd like to slightly alter the answer already given by René and add some code examples. I also recommend to put your import logic into a dedicated class, e.g. ImportService:
namespace Vendor\MyExt\Service;
use TYPO3\CMS\Core\SingletonInterface;
class ImportService implements SingletonInterface
{
public function importData()
{
// import logic goes here
}
}
You can now inject this class as a dependency of your CommandController and your ActionController:
class ImportController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionController
{
/**
* #var \Vendor\MyExt\Service\ImportService
* #inject
*/
protected $importService;
public function importAction()
{
$this->importService->importData();
}
}
class ImportCommandController extends \TYPO3\CMS\Extbase\Mvc\Controller\CommandControlle
{
/**
* #var \Vendor\MyExt\Service\ImportService
* #inject
*/
protected $importService;
public function importCommand()
{
$this->importService->importData();
}
}
The use of an CommandController in an ActionController is not recommended because they have different envoiroment variables.
If you want to use some code on more position it's recommanded to use Utility classes.
So create an Class in the my_ext/Classes/Utility/ directory call the class something like ImportUtility and try to code your import independed from some controller.
My error message:
Illuminate \ Container \ BindingResolutionException
Target [Project\Backend\Service\Validation\ValidableInterface] is not instantiable.
I understand that interfaces and abstract classes are not instantiable so I know that Laravel should not be trying to instantiate my interface. Yet somehow it's trying to and I suspect this may be a binding issue...even though I believe I have bound it correctly and have registered it as a service provider.
I should mention that I have taken this example out of Chris Fidao's "Implementing Laravel" and it's almost identical!
This is the first couple of lines of my form class:
namespace Project\Backend\Service\Form\Job;
use Project\Backend\Service\Validation\ValidableInterface;
use Project\Backend\Repo\Job\JobInterface;
class JobForm {
/**
* Form Data
*
* #var array
*/
protected $data;
/**
* Validator
*
* #var \Project\Backend\Form\Service\ValidableInterface
*/
protected $validator;
/**
* Job repository
*
* #var \Project\Backend\Repo\Job\JobInterface
*/
protected $job;
public function __construct(ValidableInterface $validator, JobInterface $job)
{
$this->validator = $validator;
$this->job = $job;
}
This is the first few lines of my validator class:
namespace Project\Backend\Service\Form\Job;
use Project\Backend\Service\Validation\AbstractLaravelValidator;
class JobFormValidator extends AbstractLaravelValidator {
// Includes some validation rules
This is the abstract validator:
namespace Project\Backend\Service\Validation;
use Illuminate\Validation\Factory;
abstract class AbstractLaravelValidator implements ValidableInterface {
/**
* Validator
*
* #var \Illuminate\Validation\Factory
*/
protected $validator;
/**
* Validation data key => value array
*
* #var Array
*/
protected $data = array();
/**
* Validation errors
*
* #var Array
*/
protected $errors = array();
/**
* Validation rules
*
* #var Array
*/
protected $rules = array();
/**
* Custom validation messages
*
* #var Array
*/
protected $messages = array();
public function __construct(Factory $validator)
{
$this->validator = $validator;
}
This is the code where I bind it all to the app:
namespace Project\Backend\Service\Validation;
use Illuminate\Support\ServiceProvider;
use Project\Backend\Service\Form\Job\JobFormValidator;
class ValidationServiceProvider extends ServiceProvider {
public function register()
{
$app = $this->app;
$app->bind('Project\Backend\Service\Form\Job\JobFormValidator', function($app)
{
return new JobFormValidator($app['validator']);
});
}
}
This is then registered in app/config/app.php:
.....
'Project\Backend\Service\Validation\ValidationServiceProvider',
....
Finally these are the first few lines of my controller:
use Project\Backend\Repo\Job\JobInterface;
use Project\Backend\Service\Form\Job\JobForm;
class JobController extends \BaseController {
protected $jobform;
function __construct(JobInterface $job, JobForm $jobform)
{
$this->job = $job;
$this->jobform = $jobform;
}
You need to tell Laravel which instance it should use for a certain interface when injecting it into the constructor via type hinting.
You do this using the bind() method (in your service provider for example)
$app->bind('JobInterface', 'Job'); // Job being the class you want to be used
I highly recommend you watch the video here where Taylor Otwell, the creator of Laravel, explains this and some other things.
First you need to bind using
/app/Providers/AppServiceProvider.php
<?php namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider {
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
//
}
/**
* Register any application services.
*
* #return void
*/
public function register()
{
//
$this->app->bind('JobInterface', 'Job');
}
}
Once you complete this change
Run composer update