I am trying to use Silex together with Doctrine ORM (not just DBAL) but I am unable to get the configuration correct.
composer.json
{
"require": {
"silex/silex": "1.0.*#dev",
"symfony/monolog-bridge": "~2.1",
"symfony/twig-bridge": "~2.1",
"symfony/form": "~2.1",
"symfony/yaml": "2.2.*",
"symfony/form": "2.2.*",
"symfony/translation": "~2.1",
"symfony/config": "2.2.*",
"dflydev/doctrine-orm-service-provider": "1.0.*#dev"
},
"autoload": {
"psr-0": {
"Entities": "src/"
}
}
}
bootstrap.php located in my project root folder
use Doctrine\ORM\Tools\Setup;
use Doctrine\ORM\EntityManager;
require_once __DIR__ ."/vendor/autoload.php";
$isDevMode = true;
$config = Setup::createAnnotationMetadataConfiguration(array(__DIR__."/src/Entities"), $isDevMode);
$params = array(
'driver' => 'pdo_sqlite',
'path' => __DIR__ . '/development.sqlite',
);
$entityManager = EntityManager::create($params, $config);
cli-config.php also located inside the root folder
require_once "bootstrap.php";
$helperSet = new \Symfony\Component\Console\Helper\HelperSet(array(
'db' => new \Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper($entityManager->getConnection()),
'em' => new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($entityManager)
));
Customer.php entity located inside src/Entities
/**
* #Entity #Table(name="customers")
**/
class Customer {
/** #Id #Column(type="integer") #GeneratedValue **/
protected $id;
/** #Column(type="string") **/
protected $name;
public function getName() {
return $this->name;
}
public function setName($name) {
$this->name = $name;
}
public function getId() {
return $this->id;
}
}
I am able to run commands like php vendor/bin/doctrine orm:schema-tool:create and have it generate a table called customs just as it should. But how do I load that entity inside my Silex application
Here is my index.php
require_once __DIR__.'/../vendor/autoload.php';
$app = new Silex\Application();
use Symfony\Component\Yaml\Yaml;
$app['config'] = function () {
$config = Yaml::parse(__DIR__ .'/../config.yml');
return $config;
};
$app->register(new Silex\Provider\DoctrineServiceProvider(), array(
'dns.options' => $app['config']['database']['development']
));
$app->register(new Dflydev\Silex\Provider\DoctrineOrm\DoctrineOrmServiceProvider, array(
'orm.em.options' => array(
'mappings' => array(
array(
'type' => 'annotation',
'path' => __DIR__ .'/src/Entities',
)
)
),
));
$app->get('/', function () use ($app) {
$customer = $app['orm.em']->getRepository('Customer');
return '<pre>'. $customer->getName() .'</pre>';
});
The result when loading the localhost inside my browser
Warning: class_parents() [function.class-parents]: Class Customer does not exist and could not be loaded in /Users/me/Documents/project/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/RuntimeReflectionService.php on line 40
UPDATE
I am not sure this is the correct way to solve this issue, but by using the following approach the problem got solved and I can now use my entities in Silex
$app['em'] = function ($app) {
$config = Setup::createAnnotationMetadataConfiguration(array(__DIR__."/src/Entities"), true);
$params = array(
'driver' => 'pdo_sqlite',
'path' => __DIR__ . '/../development.sqlite',
);
$entityManager = EntityManager::create($params, $config);
return $entityManager;
};
I used the dependency approach because that way I can use $app['config'] to store DB information and other environment specific configurations.
$customer = new \Entities\Customer();
$customer->setName('Multi Corp '. uniqid());
$app['em']->persist($customer);
$app['em']->flush();
I presume your doctrine Entity mappings reside under "/src/Entities" in the namespace \Entities. With your autoloader directive they should be accessible as \Entities\MyMappingCls.
Your problem seems to be that you don't give the fq-name of the mapping class when getting the repository. You need to give a string that can be resolved by the autoloader. Please try:
$app['orm.em']->getRepository('Entities\Customer');
You can also try to run orm:generate-proxies as they are only generated on the fly in debug mode (not so sure this is relevant).
hth
Related
public function install() {
$this->subscribeEvent(
'Enlight_Controller_Action_PostDispatchSecure_Frontend',
'onFrontendPostDispatch',
0
);
return array('success' => true, 'invalidateCache' => array('frontend'));
}
public function onFrontendPostDispatch(\Enlight_Event_EventArgs $args)
{
/** #var \Enlight_Controller_Action $controller */
$controller = $args->get('subject');
$view = $controller->View();
$view->addTemplateDir(
__DIR__ . '/Views'
);
}
I had tried to run the plugin and override Template but Shopware does not see changes in a plugin.
I am creating new file in /Views/frontend/checkout/cart_footer.tpl in plugins root.
I am also insert
{extends file='parent:frontend/checkout/cart_footer.tpl'}
line in .tpl file but still no success.
Does any one know where is a problem?
This was very easy
I just add one line
$view->loadTemplate('frontend/plugins/checkout/cart.tpl');
And change code little bit.
I am change event from Enlight_Controller_Action_PostDispatchSecure_Frontend to
Enlight_Controller_Action_PostDispatch_Frontend_Checkout
and add $view->loadTemplate('frontend/plugins/checkout/cart.tpl');
This path is related from "/Views" folder which is declared in addTemplateDir method.
Bellow is whole code, Enjoy :)
public function install() {
$this->subscribeEvent(
'Enlight_Controller_Action_PostDispatch_Frontend_Checkout',
'onFrontendPostDispatch'
);
return array('success' => true, 'invalidateCache' => array('frontend'));
}
public function onFrontendPostDispatch(\Enlight_Event_EventArgs $args)
{
/** #var \Enlight_Controller_Action $controller */
$controller = $args->get('subject');
$view = $controller->View();
$view->addTemplateDir(
__DIR__ . '/Views'
);
$view->loadTemplate('frontend/plugins/checkout/cart.tpl');
}
I created a module which creates a custom block :
<?php
/**
* Provides a 'SLS Block' Block
*
* #Block(
* id = "SLS-Subheader",
* admin_label = #Translation("SLS Subheader"),
* )
*/
namespace Drupal\subheader\Plugin\Block;
use Drupal\Core\Block\BlockBase;
class SubheaderBlock extends BlockBase {
/**
* {#inheritdoc}
*/
public function build() {
return array(
'#title' => "test",
);
}
}
?>
The module name is "subheader"
In my subheader.module i want to hook a specific template:
<?php
/**
* Implements hook_theme().
*/
function subheader_theme() {
return array(
'slssubheader' => array(
'variables' => array('pierre' => NULL),
'template' => 'specifictemplate',
),
);
}
I tried all kind of naming convention for the function name and the array key, but always unsuccesful. It never hook the template to specifictemplate.html.twig
Anyone has an idea??
Thanks a LOOOOTTT
Pierre
I had the same problem, though probably a different cause. Google lead me to your question though. The issue with your code is the missing #theme key in your build method I believe:
public function build() {
return array(
'#title' => "test",
'#theme' => 'slssubheader' // this one
);
}
In my case I had to search for a couple of hours before I found out I accidentally added a custom namespace to my .module file. Drupal doesn't like that and didn't recognize any of my hooks.
I want to test a model and for one of those tests I want to mock a method of the model I am testing. So I don't test a controller and I don't want to replace a whole model, just one method of the same model I test.
Reason is that this model method calls a file upload handler. This feature is already tested elsewhere.
What I am doing now is:
I test the model 'Content'. There I test it's method 'addTeaser', which calls 'sendTeaser'.
SO I want to mock sendTeaser and fake a successful answer of the method sendTeaser, while testing addTeaser.
That looks like this:
$model = $this->getMock('Content', array('sendTeaser'));
$model->expects($this->any())
->method('sendTeaser')
->will($this->returnValue(array('ver' => ROOT.DS.APP_DIR.DS.'webroot/img/teaser/5/555_ver.jpg')));
$data = array(
'Content' => array(
'objnbr' => '555',
'name' => '',
...
)
)
);
$result = $model->addTeaser($data);
$expected = true;
$this->assertEquals($expected, $result);
When I let my test run, I get an error that a model within the method 'sendTeaser' is not called properly. Hey! It shouldn't be called! I mocked the method!
..... or not?
What would be the proper syntax for mocking the method?
Thanks a lot as always for help!
Calamity Jane
Edit:
Here is the relevant code for my model:
App::uses('AppModel', 'Model');
/**
* Content Model
*
* #property Category $Category
*/
class Content extends AppModel {
public $dateipfad = '';
public $fileName = '';
public $errormessage = '';
public $types = array(
'sqr' => 'square - more or less squarish',
'hor' => 'horizontal - clearly wider than high',
'lnd' => 'landscape - low but very wide',
'ver' => 'column - clearly higher than wide',
);
public $order = "Content.id DESC";
public $actsAs = array('Containable');
public $validateFile = array(
'size' => 307200,
'type' => array('jpeg', 'jpg'),
);
//The Associations below have been created with all possible keys, those that are not needed can be removed
public $hasMany = array(
'CategoriesContent' => array(
'className' => 'CategoriesContent',
),
'ContentsTag' => array(
'className' => 'ContentsTag',
),
'Description' => array(
'className' => 'Description',
)
);
/**
* Saves the teaser images of all formats.
*
* #param array $data
*
* #return Ambigous <Ambigous, string, boolean>
*/
public function addTeaser($data)
{
$objnbr = $data['Content']['objnbr'];
$type = $data['Content']['teaser-type'];
if (!empty($data['Content']['teaser-img']['tmp_name'])) {
$mFileNames = $this->sendTeaser($data, $objnbr, $type);
}
if (!is_array($mFileNames)) {
$error = $mFileNames;
//Something failed. Remove the image uploaded if any.
$this->deleteMovedFile(WWW_ROOT.IMAGES_URL.$mFileNames);
return $error;
}
return true;
}
/**
* Define imagename and save the file under this name.
*
* Since we use Imagechache, we don't create a small version anymore.
*
* #param integer $objnbr
* #param string $teasername
*
* #return multitype:Ambigous <string, boolean> |Ambigous <boolean, string>
*/
public function sendTeaser($data, $objnbr, $type)
{
//$path = str_replace('htdocs','tmp',$_SERVER['DOCUMENT_ROOT']);
$this->fileName = $this->getImageName($objnbr, $type);
$oUH = $this->getUploadHandler($data['Content']['teaser-img']);
debug($oUH);
exit;
$error = $oUH->handleFileUpload();
if (empty($type))
$type = 0;
if ($error === 'none'){
// Send to ImageChacheServer
$oICC = $this->getImagecacheConnector();
$sCacheUrl = $oICC->uploadFile($objnbr, $type, $this->fileName);
debug($sCacheUrl);
return array($type => $this->fileName);
}
return $error;
}
public function getUploadHandler($imgdata)
{
App::uses('UploadHandler', 'Lib');
$oUH = new UploadHandler($this, $imgdata);
return $oUH;
}
}
Changing getMock to getMockForModel didn't change the output though.
I'd like to emphasize the answer from #ndm using Cake test helper class CakeTestCase::getMockForModel()
$theModel = CakeTestCase::getMockForModel('Modelname', ['theMethodToMock']);
$theModel->expects($this->once())
->method('theMethodToMock')
->will($this->returnValue('valueToReturn'));
$this->getMock is not the way to mock. You should use $this->generate
I would reccomend you to read a book about CakePHP unti testing, like this: https://leanpub.com/cakephpunittesting
I have the following tables: films, categories, films_categories and the entities:
Film.php
<?php
namespace Admin\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Table(name="films")
*/
class Film{
/**
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(type="integer")
* #ORM\Column(length=11)
*/
private $id;
/**
* #ORM\Column(type="string")
*/
private $name;
/* .... */
/**
* #ORM\ManyToMany(targetEntity="Category")
* #ORM\JoinTable(name="films_categories",
* joinColumns={#ORM\JoinColumn(name="film_id", referencedColumnName = "id")},
* inverseJoinColumns={#ORM\JoinColumn(name="category_id", referencedColumnName="id")})
*/
private $categories;
public function __construct(){
$this->categories = new ArrayCollection();
}
public function getCategoriesNames(){
$names = array();
foreach($this->categories as $category){
$names[] = $category->getName();
}
return $names;
}
public function getId(){
return $this->id;
}
public function setId($id){
$this->id = $id;
}
/* ... */
/**
* #return Collection
*/
public function getCategories(){
return $this->categories;
}
public function addCategories(Collection $categories){
foreach($categories as $category){
$this->categories->add($category);
}
}
public function removeCategories(Collection $categories){
foreach($categories as $category){
$this->categories->removeElement($category);
}
}
}
Category.php
<?php
namespace Admin\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Table(name="categories")
*/
class Category {
/**
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(type="integer")
*/
private $id;
/* ... */
public function getId(){
return $this->id;
}
public function setId($id){
$this->id = $id;
}
/* ... */
}
What I want to do is create a form and the action to add a new film and assign a category to it. Here is the form I used:
FilmFieldset.php
<?php
namespace Admin\Form;
use Zend\Form\Fieldset;
use Zend\InputFilter\InputFilterProviderInterface;
use DoctrineORMModule\Stdlib\Hydrator\DoctrineEntity;
use Admin\Entity\Film;
class FilmFieldset extends Fieldset implements InputFilterProviderInterface{
protected $entityManager;
public function __construct($em){
parent::__construct('film');
$this->entityManager= $em;
$this->setHydrator(new DoctrineEntity($em,'Admin\Entity\Film'))
->setObject(new Film());
#$this->setAttribute('method','post');
#$this->setAttribute('class','standardForm');
$this->add(array(
'name' => 'id',
'type' => 'hidden'
));
/* ... */
$this->add(
array(
'type' => 'DoctrineModule\Form\Element\ObjectSelect',
'name' => 'categories',
'attributes' => array(
'multiple' => 'multiple',
),
'options' => array(
'object_manager' => $em,
'target_class' => 'Admin\Entity\Category',
'property' => 'name',
'label' => 'Categories: ',
'disable_inarray_validator' => true
),
)
);
}
public function getInputFilterSpecification(){
return array(
/* .... */
'categories' => array(
'required' => true,
),
);
}
}
The FilmForm.php
<?php
namespace Admin\Form;
use Zend\Form\Form;
use Zend\Stdlib\Hydrator\ClassMethods;
use Admin\Entity\Film;
use Zend\InputFilter\InputFilter;
use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator;
class FilmForm extends Form{
public function __construct($em){
parent::__construct('filmForm');
$this->setAttribute('method','post')
->setAttribute('class','standardForm')
->setHydrator(new DoctrineHydrator($em,'\Admin\Entity\Film'))
->setInputFilter(new InputFilter());
/* I register the fieldset through a service and not directly here */
// $this->add(array(
// 'type' => new FilmFieldset($em),
// 'options' => array(
// 'user_as_base_fieldset' => true
// )
// ));
$this->add(array(
'name' => 'security',
'type' => 'Zend\Form\Element\Csrf'
));
$this->add(array(
'name' => 'submit',
'type' => 'submit',
));
$this->setValidationGroup(array(
'security',
'film' => array(
'categories',
)
));
}
}
The addAction:
public function addAction() {
$em = $this->getEntityManager();
$form = $this->getForm();
$film = new Film();
$form->bind($film);
if($request->isPost()){
$post = array_merge_recursive(
$request->getPost()->toArray(),
$request->getFiles()->toArray()
);
$form->setData($post);
if($form->isValid()){
$categories = array();
foreach($post['film']['categories'] as $categoryId){
$categories[] = $em->getRepository('Admin\Entity\Category')->find($categoryId);
}
$film->addCategories($categories);
$em->persist($film);
$em->flush();
}else{
// the form is not valid
}
}
The result is various errors and and ORMExcption with the message "Found entity of type on association Admin\Entity\Film#categories, but expecting Admin\Entity\Category"
Please help me out, I'm literally freaking out over this! Thank you :)
From what i found out from this is some thing like this that your entity is receiving Admin\Entity\Film#categories and in this part categories is some what a value. Where as your entity is expecting an object of type Admin\Entity\Film#categories.
To get over this you have to create a
public function SetCategory(Admin\Entity\Category Category)
{
$this->categories(or category or w/e your variable name is)= Category;
}
public function getCategory()
{
return $this->categories(or category or w/e your variable name is);
}
And then in your Action you have to pass object of Category to Film Entity in some thing like this
$Film->SetCategory($categoryObj);
Of course you have to set your business logic according to your part, but this error Should be removed by this apporach.
You need to define the FQCN for the targetEntity.
Change:
* #ORM\ManyToMany(targetEntity="Category")
to:
* #ORM\ManyToMany(targetEntity="Admin\Entity\Category")
This has to do with hydration and lazy loading. I'm no expert on this, but the #categories is a proxy object, and when you are going to save it, it's complaining because it needs to be an actual collection, not a proxy object to a collection.
If you did noobie-php's approach, you're re-attaching new category objects on the main object, so they're not proxies any more.
This is frustrating, as Doctrine was supposed to save a lot of the hassle of this, but it doesn't actually do what you'd naturally expect in many situations. I'd found a bug ticket on this some time ago but can't find it now - will add it to this if I can find it.
I'm unit testing my controller called IndexController (used the http://framework.zend.com/manual/2.1/en/user-guide/modules.html tutorial).
My IndexController has a dependency:Translator $translator.
<?php
// module/Application/src/Application/Controller/IndexController.php
namespace Application\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Zend\I18n\Translator\Translator;
class IndexController extends AbstractActionController
{
protected $translator;
public function __construct(Translator $translator)
{
$this->translator = $translator;
}
public function indexAction()
{
$steeringWheelMapper = $this->getServiceLocator()->get('SupplierName\Mapper\SteeringWheel');
$carBrandList = $steeringWheelMapper ->fetchCarBrandList();
return new ViewModel();
}
}
My unit test setup:
module
Application
test
ApplicationTest
Controller
IndexControllerTest.php
Bootstrap.php
phpunit.xml.dist
TestConfig.php.dist
Testing now gives the following error:
Argument 1 passed to Application\Controller\IndexController::__construct() must be an instance of Zend\I18n\Translator\Translator, none given.
Nothing strange here, but I can't find out where to add the translator object..
Where should I add the translator object?
The code
<?php
// module/Application/test/Testconfig.php.dist
return array(
'modules' => array(
'Application',
),
'module_listener_options' => array(
'config_glob_paths' => array(
'../../../config/autoload/{,*.}{global,local}.php',
),
'module_paths' => array(
'module',
'vendor',
),
),
);
<?php
// module/Application/test/Bootstrap.php
namespace ApplicationTest;//Change this namespace for your test
use Zend\Loader\AutoloaderFactory;
use Zend\Mvc\Service\ServiceManagerConfig;
use Zend\ServiceManager\ServiceManager;
use Zend\Stdlib\ArrayUtils;
use RuntimeException;
error_reporting(E_ALL | E_STRICT);
chdir(__DIR__);
class Bootstrap
{
protected static $serviceManager;
protected static $config;
protected static $bootstrap;
public static function init()
{
// Load the user-defined test configuration file, if it exists; otherwise, load
if (is_readable(__DIR__ . '/TestConfig.php')) {
$testConfig = include __DIR__ . '/TestConfig.php';
} else {
$testConfig = include __DIR__ . '/TestConfig.php.dist';
}
$zf2ModulePaths = array();
if (isset($testConfig['module_listener_options']['module_paths'])) {
$modulePaths = $testConfig['module_listener_options']['module_paths'];
foreach ($modulePaths as $modulePath) {
if (($path = static::findParentPath($modulePath)) ) {
$zf2ModulePaths[] = $path;
}
}
}
$zf2ModulePaths = implode(PATH_SEPARATOR, $zf2ModulePaths) . PATH_SEPARATOR;
$zf2ModulePaths .= getenv('ZF2_MODULES_TEST_PATHS') ?: (defined('ZF2_MODULES_TEST_PATHS') ? ZF2_MODULES_TEST_PATHS : '');
static::initAutoloader();
// use ModuleManager to load this module and it's dependencies
$baseConfig = array(
'module_listener_options' => array(
'module_paths' => explode(PATH_SEPARATOR, $zf2ModulePaths),
),
);
$config = ArrayUtils::merge($baseConfig, $testConfig);
$serviceManager = new ServiceManager(new ServiceManagerConfig());
$serviceManager->setService('ApplicationConfig', $config);
$serviceManager->get('ModuleManager')->loadModules();
static::$serviceManager = $serviceManager;
static::$config = $config;
}
public static function getServiceManager()
{
return static::$serviceManager;
}
public static function getConfig()
{
return static::$config;
}
protected static function initAutoloader()
{
$vendorPath = static::findParentPath('vendor');
if (is_readable($vendorPath . '/autoload.php')) {
$loader = include $vendorPath . '/autoload.php';
} else {
$zf2Path = getenv('ZF2_PATH') ?: (defined('ZF2_PATH') ? ZF2_PATH : (is_dir($vendorPath . '/ZF2/library') ? $vendorPath . '/ZF2/library' : false));
if (!$zf2Path) {
throw new RuntimeException('Unable to load ZF2. Run `php composer.phar install` or define a ZF2_PATH environment variable.');
}
include $zf2Path . '/Zend/Loader/AutoloaderFactory.php';
}
AutoloaderFactory::factory(array(
'Zend\Loader\StandardAutoloader' => array(
'autoregister_zf' => true,
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/' . __NAMESPACE__,
),
),
));
}
protected static function findParentPath($path)
{
$dir = __DIR__;
$previousDir = '.';
while (!is_dir($dir . '/' . $path)) {
$dir = dirname($dir);
if ($previousDir === $dir) return false;
$previousDir = $dir;
}
return $dir . '/' . $path;
}
}
Bootstrap::init();
<?php
// module/Application/test/ApplicationTest/Controller/IndexControllerTest.php
namespace ApplicationTest\Controller;
use ApplicationTest\Bootstrap;
use Zend\Mvc\Router\Http\TreeRouteStack as HttpRouter;
use Application\Controller\IndexController;
use Zend\Http\Request;
use Zend\Http\Response;
use Zend\Mvc\MvcEvent;
use Zend\Mvc\Router\RouteMatch;
use PHPUnit_Framework_TestCase;
class IndexControllerTest extends \PHPUnit_Framework_TestCase
{
protected $controller;
protected $request;
protected $response;
protected $routeMatch;
protected $event;
protected function setUp()
{
$serviceManager = Bootstrap::getServiceManager();
$this->controller = new IndexController();
$this->request = new Request();
$this->routeMatch = new RouteMatch(array('controller' => 'index'));
$this->event = new MvcEvent();
$config = $serviceManager->get('Config');
$routerConfig = isset($config['router']) ? $config['router'] : array();
$router = HttpRouter::factory($routerConfig);
$this->event->setRouter($router);
$this->event->setRouteMatch($this->routeMatch);
$this->controller->setEvent($this->event);
$this->controller->setServiceLocator($serviceManager);
}
public function testIndexActionCanBeAccessed()
{
$this->routeMatch->setParam('action', 'index');
$result = $this->controller->dispatch($this->request);
$response = $this->controller->getResponse();
$this->assertEquals(200, $response->getStatusCode());
}
}
Inject it in your setUp method
protected function setUp()
{
// ...
$translator = new \Zend\I18n\Translator;
$this->controller = new IndexController($translator);
// ....
}