Implementing GeocodableBehavior in Symfony 1.4 (using Propel) - symfony-1.4

I'm trying to implement the GeocodableBehavior on a Symfony 1.4 (with Propel 1.6) project i'm working on, but until now it's a complete failure. I've tried to search if other people but I didn't found anything, like if I was the only one having troubles with this.
So, maybe I'm missing something very very easy, but following the instructions given on the GeocodableBehavior leads to nothing but errors, and I can't figure out where's the problem.
I followed instructions for the GeocodableBehavior (here -> http://www.propelorm.org/cookbook/geocodable-behavior.html)
This seems to work as i'm getting the latitude/longitude columns created on my model. Until then, it works fine.
Where things get a little more complicated is when trying to save an object with the GeocodableBehavior, there's problems with the Geocoder class.
(Documentation here -> https://github.com/willdurand/Geocoder)
My class is Point, referring to a geolocated point, an address. When creating a Point using sf admin generator, the behavior which is supposed to use some fields (street, postal_code, country, etc) to query the GoogleMaps api, just fails to use the Geocoder class.
Fatal error: Class 'Geocoder\Geocoder' not found in /var/www/vhosts/www._________.local/lib/model/om/BasePoint.php on line 3717
I put the Geocoder class in a lib/vendor/geocoder folder, I tried to use the autoload.yml file to load it, but nothing changes...
autoload:
geocoder:
name: geocoder
path: %SF_LIB_DIR%/vendor/geocoder
recursive: on
There's something i'm missing in how to load those classes in my sf project, and i can't find what. Geocoder package has an autoload.php file but i didn't manage to "load" it successfully...
Thanks in advance.

I know it's kinda giving up on the autoloader, but you could establish a register function in /config/ProjectConfiguration.class.php. The only downside is that you will need to add a call to the function before any block that uses Geocoder.
class ProjectConfiguration extends sfProjectConfiguration
{
static protected $geocoderLoaded = false;
static public function registerGeocoder()
{
if (self::$geocoderLoaded) {
return;
}
require_once sfConfig::get('sf_lib_dir') . '/vendor/geocoder/autoload.php';
self::$geocoderLoaded = true;
}
...
}
Then just execute ProjectConfiguration::registerGeocoder(); anywhere you'd need the class. It's more annoying than getting the autoloader to work, but it's at least dependable.

Did you check your autoload cache to see it there is something related to Geocoder?
/cache/[apps_name]/dev/config/config_autoload.yml.php
/cache/project_autoload.cache
Maybe, manually add the autoload in the /config/ProjectConfiguration.class.php:
class ProjectConfiguration extends sfProjectConfiguration
{
public function setup()
{
require_once sfConfig::get('sf_lib_dir').'/vendor/geocoder/src/autoload.php';

Using the built-in autoloader should be a working option, but you can also combine symfony's autoloader with a "PSR-0 enabled" one. Basically, this boils down to the following implementation:
public function setup()
{
// plugin stuff here
// register the new autoloader
spl_autoload_register(array($this, 'autoloadNamespace'));
}
public function autoloadNamespace($className)
{
$className = ltrim($className, '\\');
$fileName = '';
$namespace = '';
if ($lastNsPos = strripos($className, '\\'))
{
$namespace = substr($className, 0, $lastNsPos);
$className = substr($className, $lastNsPos + 1);
$fileName = str_replace('\\', DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR;
}
// make sure that the path to Geocoder is correct
foreach(array(
sfConfig::get('sf_lib_dir').'/vendor/Geocoder/src' . DIRECTORY_SEPARATOR . $fileName . $className . '.php',
) as $fileName)
{
if (file_exists($fileName))
{
require $fileName;
return true;
}
}
return false;
}
With this additional autoloader, your application should be able to use Geocoder.

Related

Install Symfony without symfony/runtime and with old index.php

I am trying to add Symfony 5.4 to my legacy project. There is a pretty nice documentation on how to do this, but there's a big problem - the documentation assumes "normal" Symfony, but each time I try to install Symfony using their recommended way of composer create-project, I get a Symfony version with symfony/runtime - the big problem here, is that this version has a completely different index.php:
<?php
use App\Kernel;
require_once dirname(_DIR_).'/vendor/autoload_runtime.php';
return function (array $context) {
return new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']);
};
The documentation found here is based on a completely different index file.
I did find that I can remove the runtime package, and just copy old index, and it works for the most part, but then you also have problems with console.php and I worry that if I go this route there will be more and more problems caused by my installation expecting symfony/runtime and me manually removing it's
I tried installing Symfony 5.3 as well as different patches of 5.4, all came with this installed, even though I did work on some 5.3 / 5.4 projects and had the old school index.php file.
Does anyone know how to currently install Symfony with the "old" index.php, console.php etc.?
Thanks!
So the task is to migrate from a non-Symfony legacy app to a Symfony app. The basic idea is to allow the Symfony app to process a request and then hand it off to the legacy app if necessary. The Symfony docs show how to do this but but relies on the older style index.php file. The newer runtime based approach is a bit different.
But in the end all it really takes is a couple of fairly simple classes. A runner class takes care of creating a request object and turning it into a response. This is where you can add the bridge to your legacy app. It's a clone of Symfony's HttpKernelRunner class:
namespace App\Legacy;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\TerminableInterface;
use Symfony\Component\Runtime\RunnerInterface;
class LegacyRunner implements RunnerInterface
{
private $kernel;
private $request;
public function __construct(HttpKernelInterface $kernel, Request $request)
{
$this->kernel = $kernel;
$this->request = $request;
}
public function run(): int
{
$response = $this->kernel->handle($this->request);
// check the response to see if it should be handed off to legacy app
dd('Response Code ' . $response->getStatusCode());
$response->send();
if ($this->kernel instanceof TerminableInterface) {
$this->kernel->terminate($this->request, $response);
}
return 0;
}
}
Next you need to wire up runner by extending the SymfonyRuntime::getRunner method:
namespace App\Legacy;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\Runtime\RunnerInterface;
use Symfony\Component\Runtime\SymfonyRuntime;
class LegacyRuntime extends SymfonyRuntime
{
public function getRunner(?object $application): RunnerInterface
{
if ($application instanceof HttpKernelInterface) {
return new LegacyRunner($application, Request::createFromGlobals());
}
return parent::getRunner($application);
}
}
Finally, update composer.json to use your legacy runtime class:
"extra": {
...
"runtime": {
"class": "App\\Legacy\\LegacyRuntime"
}
}
After updating composer.json do a composer update for the changes to take effect and start your server. Navigate to a route and you should hit the dd statement.

liip imagine_filter in Symfony Controller on non public path

Is there any way to use liip imagine_filter without copying the image source to a public path?
I can not see how resolvers/loaders have to be set up to load images from a non public file location and store them likewise.
I defined a watermark filter with a watermark image placed outside public path - which works without problems. But ONLY applied on images placed IN public path.
I am on Symfony 5 and "liip/imagine-bundle": "^2.6"
I have the same problem as you and i found this solution, of course is a little workaround but it works very well.
So I copy the file from $remoteWatermak into the server. if the $localWatermark is setted I check if the file exist.
$arrContextOptions=array(
"ssl"=>array(
"verify_peer"=>false,
"verify_peer_name"=>false,
),
);
if($localWatermark){
$filesystem = new Filesystem();
if(!$filesystem->exists($localWatermark)){
$contents = file_get_contents($remoteWatermark,false,
stream_context_create($arrContextOptions)); // get file
file_put_contents($localWatermark , $contents);
}
}else{
$contents = file_get_contents($remoteWatermark,false,
stream_context_create($arrContextOptions)); // get file
$file = "uploads/watermarks/" . uniqid();
file_put_contents($file , $contents);
$localWatermark = $file;
}

Can I include an optional config file in Symfony2?

I want to make a local config file, config_local.yml, that allows each development environment to be configured correctly without screwing up other people's dev environments. I want it to be a separate file so that I can "gitignore" it and know that nothing essential is missing from the project, while simultaneously not having the issue of git constantly telling me that config_dev.yml has new changes (and running the risk of someone committing those changes).
Right now, I have config_dev.yml doing
imports:
- { resource: config_local.yml }
which is great, unless the file doesn't exist (i.e. for a new clone of the repository).
My question is: Is there any way to make this include optional? I.e., If the file exists then import it, otherwise ignore it.
Edit: I was hoping for a syntax like:
imports:
- { resource: config.yml }
? { resource: config_local.yml }
I know this is a really old question, and I do think the approved solution is better I thought I would give a simpler solution which has the benefit of not changing any code
You can use the ignore_errors option, which won't display any errors if the file doesn't exist
imports:
- { resource: config_local.yml, ignore_errors: true }
Warning, if you DO have a syntax error in the file, it will also be ignored, so if you have unexpected results, check to make sure there is no syntax error or other error in the file.
There is another option.
on app/appKernel.php change the registerContainerConfiguration method to this :
public function registerContainerConfiguration(LoaderInterface $loader)
{
$loader->load(__DIR__.'/config/config_'.$this->getEnvironment().'.yml');
$extrafiles = array (
__DIR__.'/config/config_local.yml',
);
foreach ($extrafiles as $filename) {
if (file_exists($filename) && is_readable($filename)) {
$loader->load($filename);
}
}
}
this way you have a global config_local.yml file that overwrites the config_env.yml files
A solution is to create a separate environment, which is explained in the Symfony2 cookbook. If you do not wish to create one, there is another way involving the creation of an extension.
// src/Acme/Bundle/AcmeDemo/DepencendyInjection/AcmeDemoExtension.php
namespace Acme\DemoBundle\DependencyInjection;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
class AcmeDemoExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
// All following files will be loaded from the configuration directory
// of your bundle. You may change the location to /app/ of course.
$loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
try
{
$loader->load('config_local.yml');
}
catch(\InvalidArgumentException $e)
{
// File was not found
}
}
}
Some digging in the Symfony code revealed me that YamlFileLoader::load() FileLocator::locate() will throw \InvalidArgumentException, if a file is not found. It is invoked by YamlFileLoader::load().
If you use the naming conventions, the extension will be automatically executed. For a more thorough explanation, visit this blog.
I tried both above answers but none did work for me.
i made a new environment: "local" that imports "dev", but as you can read here: There is no extension able to load the configuration for "web_profiler" you also had to hack the AppKernel class.
Further you couldnt set config_local.yml to .gitignore because the file is necessary in local env.
Since i had to hack the AppKernel anyway i tried the approach with the $extrafiles but that resulted in "ForbiddenOverwriteException"
So now what worked for me was a modification of the $extrafiles approach:
replace in app/AppKernel.php
$loader->load(__DIR__ . '/config/config_' . $this->getEnvironment() . '.yml');
with
if ($this->getEnvironment() == 'dev') {
$extrafiles = array(
__DIR__ . '/config/config_local.yml',
);
foreach ($extrafiles as $filename) {
if (file_exists($filename) && is_readable($filename)) {
$loader->load($filename);
}
}
} else {
$loader->load(__DIR__ . '/config/config_' . $this->getEnvironment() . '.yml');
}

How do I read configuration settings from Symfony2 config.yml?

I have added a setting to my config.yml file as such:
app.config:
contact_email: somebody#gmail.com
...
For the life of me, I can't figure out how to read it into a variable. I tried something like this in one of my controllers:
$recipient =
$this->container->getParameter('contact_email');
But I get an error saying:
The parameter "contact_email" must be
defined.
I've cleared my cache, I also looked everywhere on the Symfony2 reloaded site documentation, but I can't find out how to do this.
Probably just too tired to figure this out now. Can anyone help with this?
Rather than defining contact_email within app.config, define it in a parameters entry:
parameters:
contact_email: somebody#gmail.com
You should find the call you are making within your controller now works.
While the solution of moving the contact_email to parameters.yml is easy, as proposed in other answers, that can easily clutter your parameters file if you deal with many bundles or if you deal with nested blocks of configuration.
First, I'll answer strictly the question.
Later, I'll give an approach for getting those configs from services without ever passing via a common space as parameters.
FIRST APPROACH: Separated config block, getting it as a parameter
With an extension (more on extensions here) you can keep this easily "separated" into different blocks in the config.yml and then inject that as a parameter gettable from the controller.
Inside your Extension class inside the DependencyInjection directory write this:
class MyNiceProjectExtension extends Extension
{
public function load( array $configs, ContainerBuilder $container )
{
// The next 2 lines are pretty common to all Extension templates.
$configuration = new Configuration();
$processedConfig = $this->processConfiguration( $configuration, $configs );
// This is the KEY TO YOUR ANSWER
$container->setParameter( 'my_nice_project.contact_email', $processedConfig[ 'contact_email' ] );
// Other stuff like loading services.yml
}
Then in your config.yml, config_dev.yml and so you can set
my_nice_project:
contact_email: someone#example.com
To be able to process that config.yml inside your MyNiceBundleExtension you'll also need a Configuration class in the same namespace:
class Configuration implements ConfigurationInterface
{
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root( 'my_nice_project' );
$rootNode->children()->scalarNode( 'contact_email' )->end();
return $treeBuilder;
}
}
Then you can get the config from your controller, as you desired in your original question, but keeping the parameters.yml clean, and setting it in the config.yml in separated sections:
$recipient = $this->container->getParameter( 'my_nice_project.contact_email' );
SECOND APPROACH: Separated config block, injecting the config into a service
For readers looking for something similar but for getting the config from a service, there is even a nicer way that never clutters the "paramaters" common space and does even not need the container to be passed to the service (passing the whole container is practice to avoid).
This trick above still "injects" into the parameters space your config.
Nevertheless, after loading your definition of the service, you could add a method-call like for example setConfig() that injects that block only to the service.
For example, in the Extension class:
class MyNiceProjectExtension extends Extension
{
public function load( array $configs, ContainerBuilder $container )
{
$configuration = new Configuration();
$processedConfig = $this->processConfiguration( $configuration, $configs );
// Do not add a paramater now, just continue reading the services.
$loader = new YamlFileLoader( $container, new FileLocator( __DIR__ . '/../Resources/config' ) );
$loader->load( 'services.yml' );
// Once the services definition are read, get your service and add a method call to setConfig()
$sillyServiceDefintion = $container->getDefinition( 'my.niceproject.sillymanager' );
$sillyServiceDefintion->addMethodCall( 'setConfig', array( $processedConfig[ 'contact_email' ] ) );
}
}
Then in your services.yml you define your service as usual, without any absolute change:
services:
my.niceproject.sillymanager:
class: My\NiceProjectBundle\Model\SillyManager
arguments: []
And then in your SillyManager class, just add the method:
class SillyManager
{
private $contact_email;
public function setConfig( $newConfigContactEmail )
{
$this->contact_email = $newConfigContactEmail;
}
}
Note that this also works for arrays instead of scalar values! Imagine that you configure a rabbit queue and need host, user and password:
my_nice_project:
amqp:
host: 192.168.33.55
user: guest
password: guest
Of course you need to change your Tree, but then you can do:
$sillyServiceDefintion->addMethodCall( 'setConfig', array( $processedConfig[ 'amqp' ] ) );
and then in the service do:
class SillyManager
{
private $host;
private $user;
private $password;
public function setConfig( $config )
{
$this->host = $config[ 'host' ];
$this->user = $config[ 'user' ];
$this->password = $config[ 'password' ];
}
}
I have to add to the answer of douglas, you can access the global config, but symfony translates some parameters, for example:
# config.yml
...
framework:
session:
domain: 'localhost'
...
are
$this->container->parameters['session.storage.options']['domain'];
You can use var_dump to search an specified key or value.
In order to be able to expose some configuration parameters for your bundle you should consult the documentation for doing so. It's fairly easy to do :)
Here's the link: How to expose a Semantic Configuration for a Bundle
Like it was saying previously - you can access any parameters by using injection container and use its parameter property.
"Symfony - Working with Container Service Definitions" is a good article about it.
I learnt a easy way from code example of http://tutorial.symblog.co.uk/
1) notice the ZendeskBlueFormBundle and file location
# myproject/app/config/config.yml
imports:
- { resource: parameters.yml }
- { resource: security.yml }
- { resource: #ZendeskBlueFormBundle/Resources/config/config.yml }
framework:
2) notice Zendesk_BlueForm.emails.contact_email and file location
# myproject/src/Zendesk/BlueFormBundle/Resources/config/config.yml
parameters:
# Zendesk contact email address
Zendesk_BlueForm.emails.contact_email: dunnleaddress#gmail.com
3) notice how i get it in $client and file location of controller
# myproject/src/Zendesk/BlueFormBundle/Controller/PageController.php
public function blueFormAction($name, $arg1, $arg2, $arg3, Request $request)
{
$client = new ZendeskAPI($this->container->getParameter("Zendesk_BlueForm.emails.contact_email"));
...
}
Inside a controller:
$this->container->getParameter('configname')
to get the config from config/config.yaml:
parameters:
configname: configvalue

PHPUnit inclusion path issues

This one's got me stumped. I've been working with PHPUnit for a couple of months now, so I'm not that green...but I look forward to being pointed in the direction of the obvious mistake I'm making! The initialisation process outlined below works fine if I run the "app" from a browser - but PHPUnit is choking...can any one put me out of my misery?
I'm trying to test a homebrew MVC, for study purposes. It follows a typical ZF layout.
Here's the index page:
include './../library/SKL/Application.php';
$SKL_Application = new SKL_Application();
$SKL_Application->initialise('./../application/configs/config.ini');
Here's the application class (early days...)
include 'bootstrap.php';
class SKL_Application {
/**
* initialises the application
*/
public function initialise($file) {
$this->processBootstrap();
//purely to test PHPUnit is working as expected
return true;
}
/**
* iterates over bootstrap class and executes
* all methods prefixed with "_init"
*/
private function processBootstrap() {
$Bootstrap = new Bootstrap();
$bootstrap_methods = get_class_methods($Bootstrap);
foreach ($bootstrap_methods as $method) {
if(substr($method,0,5) == '_init'){
$bootstrap->$method();
}
}
return true;
}
}
Here's the test:
require_once dirname(__FILE__).'/../../../public/bootstrap.php';
require_once dirname(__FILE__).'/../../../library/SKL/Application.php';
class SKL_ApplicationTest extends PHPUnit_Framework_TestCase {
protected $object;
protected function setUp() {
$this->object = new SKL_Application();
}
/**
* Tears down the fixture, for example, closes a network connection.
* This method is called after a test is executed.
*/
protected function tearDown() {
}
public function testInitialise() {
$this->assertType('boolean',$this->object->initialise());
}
}
But I keep stumbling at the first hurdle!!
PHP Warning: include(bootstrap.php): failed to open stream:
No such file or directory in path\to\files\SKL\Application.php on line 9
any ideas?
Use include_once or better yet require_once instead of include to include the bootstrap.php in the Application class file. Despite being already loaded include loads it again but since it's obviously not on the include path you get the error.
Thanks to Raoul Duke for giving me a push in the right direction, here's where I got to so far
1 - add the root of the application to the include path
2 - make all inclusion paths relative to the root of the application
3 - include a file in your unit tests that performs the same function, but compensates for the relative location when it is included. I just used realpath() on the directory location of the files.
The problem I have now is that the darn thing won't see any additional files I'm trying to pass it.
So, I'm trying to test a configuration class, that will parse a variety of filetypes dynamically. The directory structure is like this:
Application_ConfigTest.php
config.ini
The first test:
public function testParseFile() {
$this->assertType('array',$this->object->parseFile('config.ini'));
}
The error:
failed to open stream: No such file or directory
WTF? It's IN the same directory as the test class...
I solved this by providing an absolute (i.e. file structure) path to the configuration file.Can anyone explain to me how PHPUnit resolves it's paths, or is it because the test class itself is included elsewhere, rendering relative paths meaningless?

Resources