I'm trying to figure out what is going on below:
public function serviceAction(){
$config = Zend_Registry::get('config');
$client = new Zend_Http_Client();
$client->setAuth($config['api']['username'],$config['api']['password']);
$service = new Krixton_JsonRpc_Client($config['api']['endpoint'], $client);
switch($this->_getParam('task'))
{
case 'test':
if(!this->getParam('newsletter_id')){
$this->_helper->json(array('sent'=>false,'error'=>'Newsletter ID is invalid, must be numeric'));
return;
}
$request = $service->call('newsletter.send', array($this->_getParam('newsletter_id'),false));
$this->_helper->json($request->result));
break;
}
}
What I'm trying to find out is how does
`Zend_Registry::get('config')`, $client->setAuth and $service->call`
works?
I understand _getParam('task') is a method to get get or post variables but not sure about the others. I had a look through some Zend documentations but if someone could help me out that would be appreciated!
Two things are happening there, the first one is Zend_Registry. get()allow you to get a value previously registered in the registry via Zend_Registry::set('key', $value). Usually, 'config' is your application configuration, which is the application.ini file.
basically, you would register config using this bootstrap method:
protected function _initConfig()
{
$config = new Zend_Config($this->getOptions());
Zend_Registry::set('config', $config);
return $config;
}
The second ones are nothing else than methods of Zend_Http_Client. setAuth()is used to set a basic HTTP authentification, and call()is an internal method of your object Krixton_JsonRpc_Client.
If you're trying to understand deep down how does these methods work, you should read the man first (especially Zend_registry and Zend_Http_Client pages) and then read carefully the source code.
Zend_Registry::get('config') ('config' is name of an array in this case) is recalling data that was saved to the registry, probably in the Bootstrap.php to make the information in the application.ini(configuration file) available everywhere.
The Bootstrap.php probably caontains something similar to:
protected function _initRegistry() {
//make application.ini configuration available in registry
$config = new Zend_Config($this->getOptions());
Zend_Registry::set('config', $config);
}
$client->setAuth is simply providing user credentials to Zend_Http_Client() HTTP LINK that were stored in the configuration file and accessed through the $config array.
$service->call I'm quite sure what this doing because I'm not familiar with the class (likely custom) being used. It looks like a request for a newsletter is being made based on 'id'.
Related
I have a controller has an action that looks something like this:
/**
* #Route("/my_route_path", name="my_route_name")
*/
public function doSomethingAction(Request $request)
{
$myPath = $request->getScheme().'://'.$request->getHttpHost().''.$request->getBasePath();
$data = file_get_contents($myPath. '/data_folder/data.json');
return $this->render('#Entry/my_template.html.twig', array(
'data' => json_decode($data, true)
));
}
And I create a functional test for this controller like this:
/** #test */
public function doSomething_should_success()
{
$client = static::createClient();
$crawler = $client->request('GET', '/my_route_path');
$this->assertEquals(200, $client->getResponse()->getStatusCode());
}
But I can't run the functional test I still get : Failed asserting that 500 is identical to 200
So, after I checked the test.log file I find this error : file_get_contents(http://localhost/data_folder/data.json) : failed to open stream
As now the problem is comming from $request->getBasePath() because always contain empty string but the expected behaviour is return PATH_TO_MY_PROJECT_FOLDER\web in my case must return projects\web_apps\MY_PROJECT_FOLDER_NAME\web
So, the simplified question: why the request object always contain an empty basePath string in the unit test but it works very well on the browser.
The Request object helps you handle the request of a client, that is something like GET /my_route_path plus lots of headers and a server that is directed at.
The web server passes those information on to php and symfony, and symfony will turn this into a Request object. Symfony has usually one entry point, which is public/index.php (symfony 4) or web/app.php (symfony 3) which is assumed to be / or possibly /basePath/ (the basepath will be communicated by the web server and handled by Symfony).
Symfony will generate a Request object, where the basepath is essentially abstracted away, and whenever you generate a url (via Controller::generateUrl) the base path is taken into account. that's why the basepath is important for Requests.
This is actually described pretty well in the comments of the Request's functions:
getBasePath vs getPathInfo.
However, this only concerns the public facing URLs and doesn't have anything to do with how you structure your project and where that project is located, because that's completely irrelevant to the Request (separation of concerns and stuff).
So I guess, you are actually looking for the root directory of your project.
To find the location of your project dir, there is the very base version, where you directly use the PHP magic var __DIR__ which contains the directory the current script file is in, and you can navigate from there. since controllers are usually located such that their path is projectdir/src/Controller/TheController.php a __DIR__.'/../.. would give you the projectdir. However, that's not really clean. The better version:
Depending on the symfony version you're using, you should retrieve the project dir via the ParameterBagInterface (symfony 4)
function doSomethingAction(ParameterBagInterface $params) {
$projectDir = $params->get('kernel.project_dir');
}
or via the container (symfony 3) see also: new in symfony 3.3: A simpler way to get the project root directory
function doSomethingAction() {
$projectDir = $this->getParameter('kernel.project_dir');
}
In my case I had to inyect RequestStack $stackand access the main request, after that my "BasePath" has value. This is because I where in a subrequest and I had to access to the top level of the request.
This post helped me to understood: Symfony2 - get main request's current route in twig partial/subrequest
/**
* #Route("/myroute", name="myroute")
*/
public function myroute(RequestStack $stack)
{
$request = $stack->getMainRequest();
$route = $request->getPathInfo();
}
I'm wondering what is the best way to inject dynamic configuration(retrieved from db for instance) into configuration array in Zend Framework 2? In Module.php I have:
public function onBootstrap(MvcEvent $e) {
$eventManager = $e->getApplication()->getEventManager();
$moduleRouteListener = new ModuleRouteListener();
$moduleRouteListener->attach($eventManager);
$eventManager->attach('route', array($this, 'mergeDynamicConfig'));
}
public function mergeDynamicConfig(EventInterface $e) {
$application = $e->getApplication();
$sm = $application->getServiceManager();
$configurationTable = $sm->get('DynamicConfiguration\Model\Table\ConfigurationTable');
$dynamicConfig = $configurationTable->fetchAllConfig();
//Configuration array from db
//Array
//(
// [config] => 'Test1',
// [config2] => 'Test2',
// [config3] => 'Test3',
//)
//What to do here?
//I want to use the configurations above like $sm->get('Config')['dynamic_config']['config3'];
}
There is a section in the documentation that explains how to manipulate the merged configuration using the specific event ModuleEvent::EVENT_MERGE_CONFIG
Zend\ModuleManager\Listener\ConfigListener triggers a special event, Zend\ModuleManager\ModuleEvent::EVENT_MERGE_CONFIG, after merging all configuration, but prior to it being passed to the ServiceManager. By listening to this event, you can inspect the merged configuration and manipulate it.
The problem with this is that the service manager is not available at this point as the listener's event is one of the first events triggered by the module manager at priority 1000).
This means that you cannot execute your query and merge the config prior to the configuration being passed to the service manager, you would need to do so after.
Perhaps I have misunderstood your requirements, however I would approach this differently.
You could replace any calls where you need config $serviceManager->get('config') with $serviceManager->get('MyApplicationConfig'); which would be you own configuration service that uses the merged application config and then adds to it.
For example, you could register this configuration service in module.config.php.
return [
'service_manager' => [
'factories' => [
'MyApplicationConfig' => 'MyApplicationConfig\Factory\MyApplicationConfigFactory',
]
],
];
And create a factory to do the loading of merged module configuration, making any database calls or caching etc.
class MyApplicationConfigFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $sm)
{
$config = $sm->get('config');
$dbConfig = $this->getDatabaseConfigAsArray($sm);
return array_replace_recursive($config, $dbConfig);
}
protected function getDatabaseConfigAsArray(ServiceLocatorInterface $sm)
{}
}
You also have the added benefit that the service is lazy loaded.
I would not use this approuch, for a few reasons.
Putting SQL queries in your Module.php means that they will get executed on EVERY request for every user thus making your application slow, very slow.
If your database is compromised all the config will be stolen as well.
Solution would be to move all the config in your config/autoload/my_custom_config.local.php via array with keys. From there you can always load it without making a single database request. It will be way faster and secure, because the file will be outside your root folder and hacking a server is always alot harder than hacking a database.
If you still want to allow users to eit the options you can simply include the file in an action and show it with a foreach for example. To save the information you can do this:
file_put_contents("my_custom_config.local.php", '<?php return ' . var_export($config, true).';');
One other plus is that if you load your config the way discribe above you can also retrive the config like you want via $sm->get('Config')['dynamic_config']['config3']
I've been looking for an explanation on how to integrate Doctrine 2 and Zend Framework 1.12 (or 1.11, or another --I don't really know whether it matters or not but what I'm using is 1.12). I could find several blog posts and even solved questions right here in Stack Overflow but after read them one and all, I couldn't get to get what I was after: do it in a modular application. So, I'd be very grateful if somebody could give me the keys to achieve this.
Thank you very much!
EDIT:
Thank you guys for your replies but the recent release of ZF2 made me to decide to leave ZF1 in order to take advantage of all new improvements and features. As #KTastrophy said, integrating ZF and Doctrine is quite much easier now (I'd even dare to say that everything is easier and more consistent with ZF2). Thank you one more time!
It's easy to integrate doctrine 2 with ZF using the doctrine PEAR installation. After installing you just need to put this in your bootstrap:
protected function _initDoctrine() {
require_once "Doctrine/ORM/Tools/Setup.php";
\Doctrine\ORM\Tools\Setup::registerAutoloadPEAR();
$options = $this->getOptions();
$loader = new \Doctrine\Common\ClassLoader('YourNamespace', realpath(APPLICATION_PATH . "/../library"));
$loader->register();
$isDevMode = (APPLICATION_ENV == 'production') ? false: true;
$entityManager = \Doctrine\ORM\EntityManager::create(
$options['doctrine']['dbal'],
\Doctrine\ORM\Tools\Setup::createYAMLMetadataConfiguration(array(
realpath(APPLICATION_PATH."/../library/YourNamespace/Yaml"),
), $isDevMode)
);
Zend_Registry::set('entityManager', $entityManager);
return $entityManager;
}
The $this->getOptions() retrieves the database name, user and password from the config file.
If you take this tutorial as an example
http://christian.soronellas.es/2010/12/19/zend-framework-and-doctrine-2/?lang=en
See this part of the configuration code
$config = new Configuration();
$config -> setMetadataCacheImpl($cache);
$driverImpl = $config -> newDefaultAnnotationDriver($options['entitiesPath']);
$config -> setMetadataDriverImpl($driverImpl);
$config -> setQueryCacheImpl($cache);
$config -> setProxyDir($options['proxiesPath']);
$config -> setProxyNamespace('Application\Models\Proxies');
$config -> setAutoGenerateProxyClasses(('development' == APPLICATION_ENV));
$em = EntityManager::create( $this -> _buildConnectionOptions($options), $config );
The function newDefaultAnnotationDriver actually takes an array of entitites path. This creates the opportunity for you to get creative. When I found out about this, I simply created an entity folder in each module and pass each path along the newDefaultAnnotationDriver parameter in an array. Ofcourse by doing this, you will need to set the namespace per module.
I use Bisna
You should apply this patch https://github.com/guilhermeblanco/ZendFramework1-Doctrine2/pull/45
And that works well for me.
In the controller I have this function for retrieve the Entity Manager
/**
* Retrieve the Doctrine Container.
*
* #return Doctrine\ORM\EntityManager
*/
public function getEntityManager()
{
return $this->getInvokeArg('bootstrap')->getResource('doctrine')->getEntityManager();
}
I am trying to use a SOAP Client-Server in my computer and it doesn't look like it is going to work, I am getting this error Error Fetching Http Headers when I try to run my SOAP Client.
I have been looking and the solution that I have encountred is to increase the default_socket_timeout from 60 to 120 seconds and it doesn't work for me, also I have seen another solution that is putting the vhost in my apache KeepAlive Off and that didn't work.
The WSDL is working fine because I try to use it in another computer and it work.
I am running PHP Version 5.3.5-1ubuntu7.4 in Linux Mint using Zend Framework, I hope some of you can help me fix this thank you.
I'm sorry but I don't know what you are using to set up your SOAP service.....
If you can give more information about your SOAP service (poss Zend_Soap given the Zend Framework tag) etc that would be great.
Also, as a quick alternative, you say you've looked at the WSDL on another computer, perhaps try the application in an alternative environment to ensure it's not an environment issue.
May be a simple issue with your client-server code.
UPDATE: Ok so I realised the example I mentioned yesterday wasn't fully implemented so I've hacked something together quickly that you can try to see if it works in your environment.
The code is a mix of something I found here (an example of Zend_Soap_Server) and something from another SO question here (an example of a basic SOAP service test).
I've tested it at my end using ZF 1.11 and the example I'm outlining uses the default Application path you get with a new ZF project (e.g models are in directory application/models so the model shown is headed up Application_Model_Classname).
If it works, you can tweak accordingly....if it doesn't work we can try something else.
Start by creating a new SOAP controller and set the class up like this:
<?php
class SoapController extends Zend_Controller_Action
{
public function init()
{
ini_set("soap.wsdl_cache_enabled", "0"); //disable WSDL caching
$this->_helper->layout()->disableLayout(); //disable the layout
$this->_helper->viewRenderer->setNoRender(); //disable the view
}
public function indexAction ()
{
if (isset($_GET['wsdl'])) {
//return the WSDL
$this->handleWSDL();
} else {
//handle SOAP request
$this->handleSOAP();
}
}
private function handleWSDL ()
{
$strategy = new Zend_Soap_Wsdl_Strategy_AnyType();
$autodiscover = new Zend_Soap_AutoDiscover();
$autodiscover->setComplexTypeStrategy($strategy);
$autodiscover->setClass('Application_Model_SoapService');
$autodiscover->handle();
}
private function handleSOAP ()
{
$server = new Zend_Soap_Server(null,
array('uri' => "http://YOURDOMAIN/soap?wsdl"));
$server->setClass("Application_Model_SoapService");
$server->handle();
}
public function testAction()
{
$client = new Zend_Soap_Client("http://YOURDOMAIN/soap?wsdl");
try {
echo $client->testMethod('test');
} catch (Exception $e) {
echo $e;
}
}
}
In the class above, the WSDL is automatically generated using Zend_Soap_Autodiscover with a SoapService.php file at application/models/SoapService.php used as the template. Note the DocBock comments above each method in your target class are integral to this process.
Next create the SoapService.php file in the default models folder:
<?php
class Application_Model_SoapService
{
/**
* testMethod
*
* #param string $string
* #return string $testSuccess
*/
public function testMethod(string $string)
{
$testSuccess = 'Test successful, the message was: ' . $string;
return $testSuccess;
}
}
If all is working as it should be you can visit:
http://YOURDOMAIN/soap?wsdl
to see the WSDL and visit:
http://YOURDOMAIN/soap/test
to get a success message with the string you specified in the client request within the testAction() code in the SoapController class as part of the message.
Let me know if it's working or not and we can go from there.
I'll be able to have another look on Monday.
I'm pretty much newbie in Zend Framework action helpers and I am trying to use them with no success (I read a bunch of posts about action helpers, including http://devzone.zend.com/article/3350 and found no solution in like 8 hours). I used Zend Tool to setup my project and the name of the helper is Action_Helper_Common. No matter what I do, I get following error "Fatal error: Class 'Action_Helper_Common' not found". Currently, I have things set up like this:
zf version: 1.11.3
helper name: Action_Helper_Common
helpers location:
/application/controllers/helpers/Common.php
In Bootstrap.php i have following function:
protected function _initActionHelpers() {
Zend_Controller_Action_HelperBroker::addPath(APPLICATION_PATH . '/controllers/helpers', 'Action_Helper');
Zend_Controller_Action_HelperBroker::addHelper(
new Action_Helper_Common(null, $session)
);
}
I also tried this without success (it was defined in Bootstrap.php before _initActionHelpers):
protected function _initAutoloader() {
$moduleLoader = new Zend_Application_Module_Autoloader(array(
'namespace' => '',
'basePath' => APPLICATION_PATH . '/controllers/helpers'));
return $moduleLoader;
}
So what am I doing wrong?!?! PLZ help, I am desperate and about to give up :)
You got error because you haven't setup autoloader for Action_Helper_*
Resource autoloader
Helper broker uses plugin loader to load helpers based on paths and prefixes you specified to it. That is why ::getHelper() can find your helper
you dont need to do (so remove it)
Zend_Controller_Action_HelperBroker::addHelper(
new Action_Helper_Common(null, $session)
); ,
since you already did
Zend_Controller_Action_HelperBroker::addPath(APPLICATION_PATH . '/controllers/helpers', 'Action_Helper');
when you will do
$myhelper = $this->getHelper('Common');
in your controller zf will lookinto directory /controllers/helpers/ for class name Action_Helper_Common and create an instance for you and return it.
For some reason the following line didn't work for me as well:
Zend_Controller_Action_HelperBroker::addHelper( new Action_Helper_Common() );
I just keep getting a 'Class not found' error each time I'm creating a new helper object explicitly.
This is what works for me:
Zend_Controller_Action_HelperBroker::getHelper('Common');
In this case, new Action_Helper_Common object gets created and is registered with Helper Broker.
Not sure though if it works for you, since you have a parameterized constructor.