Using doctrine in a custom task - doctrine

I'm creating a task in a symfony 1.4 project, and I need to update some tables.
I've written :
<?php
class dataImportTask extends sfBaseTask
{
public function configure()
{
$this->namespace = 'data';
$this->name = 'import';
$this->addOptions(array(
new sfCommandOption('env', null, sfCommandOption::PARAMETER_REQUIRED, 'environment', 'dev'),
));
}
public function execute($arguments = array(), $options = array())
{
$databaseManager = new sfDatabaseManager(sfProjectConfiguration::getApplicationConfiguration('frontend', $options['env'], true));
$connection = $databaseManager->getDatabase()->getConnection();
}
}
(Following the example found on symfony-project.org)
But when I execute the task, symfony says : "Database "default" does not exist.". Why doesn't the task uses the dbname defined in databases.yml ?

It's a common problem when working with task. You have 2 options:
named your doctrine connection default in the database.yml
keep the same name in the database.yml and add an option on every tasks to retrieve the right database name.
For option #2, you have to add an option with the name (the one define in your database.yml) of your database and then change the way the getDatabase works:
In the database.yml:
all:
doctrine:
class: sfDoctrineDatabase
Based on the doctrine name above, add the option in your task:
<?php
class dataImportTask extends sfBaseTask
{
public function configure()
{
$this->namespace = 'data';
$this->name = 'import';
$this->addOptions(array(
new sfCommandOption('env', null, sfCommandOption::PARAMETER_REQUIRED, 'environment', 'dev'),
new sfCommandOption('connection', null, sfCommandOption::PARAMETER_REQUIRED, 'The connection name', 'doctrine'),
));
}
public function execute($arguments = array(), $options = array())
{
$databaseManager = new sfDatabaseManager(sfProjectConfiguration::getApplicationConfiguration('frontend', $options['env'], true));
$connection = $databaseManager->getDatabase($options['connection'])->getConnection();
}
}

Related

SwaggerDecorator not working after update API Platform to v2.3.5

After the API platform upgrade, the decorator from the documentation has stopped working:
https://api-platform.com/docs/core/swagger/#overriding-the-swagger-documentation
Does anyone know if this is a change, is it a bug?
I use Symfony 4.2.2 (probably the problem is due to the Symfony update).
My code adding to swagger input form to change context:
<?php
namespace App\Swagger;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
final class SwaggerDecorator implements NormalizerInterface
{
private $decorated;
public function __construct(NormalizerInterface $decorated)
{
$this->decorated = $decorated;
}
public function normalize($object, $format = null, array $context = [])
{
$docs = $this->decorated->normalize($object, $format, $context);
$customDefinition = [
'name' => 'context',
'definition' => 'Context field',
'default' => '',
'in' => 'query',
];
// Add context parameter
foreach ($docs['paths'] as $key => $value) {
// e.g. add a custom parameter
$customDefinition['default'] = lcfirst($docs['paths'][$key]['get']['tags'][0] ?? '');
$docs['paths'][$key]['get']['parameters'][] = $customDefinition;
if(isset($docs['paths'][$key]['post'])){
$docs['paths'][$key]['post']['parameters'][] = $customDefinition;
}
if(isset($docs['paths'][$key]['put'])){
$docs['paths'][$key]['put']['parameters'][] = $customDefinition;
}
}
return $docs;
}
public function supportsNormalization($data, $format = null)
{
return $this->decorated->supportsNormalization($data, $format);
}
}
Try to use parameter "decoration_priority" in services configuration (https://symfony.com/doc/current/service_container/service_decoration.html#decoration-priority)
For example:
App\Swagger\SwaggerDecorator:
decorates: 'api_platform.swagger.normalizer.documentation'
arguments: [ '#App\Swagger\SwaggerDecorator.inner' ]
decoration_priority: 1000
Or fix version "symfony/dependency-injection": "4.2.1" in composer.json )
See https://github.com/symfony/symfony/issues/29836 for details

Setting a table name in a model?

Im trying to pass in a table name to my model, as the model operates on two tables, but has the same methods.
I do it like so:
$this->model = new Emotions(array('section' => 'red'));
And in the model I set the table like:
public function __construct($attributes = array(), $exists = false){
parent::__construct($attributes, $exists);
$this->table = $attributes['section'];
}
But I get the error:
Undefined index: section
Any ideas where I'm going wrong?
Yes i get it, This class maybe running twice.
Please try this.
public function __construct($attributes = array(), $exists = false){
parent::__construct($attributes, $exists);
if(isset($attributes['section'])) {
$this->table = $attributes['section'];
}
}
My personal suggestion
<?php
class Emotions extends Eloquent
{
public function setTableName($name)
{
$this->table = $name;
return $this;
}
}
And you can use like this
$emotion = new Emotions(array('foo' => 'bar'))
->setTableName('blabla')
->save();
add below line to your class.
protected $fillable = array('section');
http://laravel.com/docs/eloquent#mass-assignment

Phalcon validation scenario

I used to use Yii framework. I would like to make project using Phalcon. I could not find validation scenario on Phalcon. What is the best way to correctly implement it on Phalcon?
Thanks in advance.
Any data validation:
<?php
use Phalcon\Validation\Validator\PresenceOf,
Phalcon\Validation\Validator\Email;
$validation = new Phalcon\Validation();
$validation->add('name', new PresenceOf(array(
'message' => 'The name is required'
)));
$validation->add('email', new PresenceOf(array(
'message' => 'The e-mail is required'
)));
$validation->add('email', new Email(array(
'message' => 'The e-mail is not valid'
)));
$messages = $validation->validate($_POST);
if (count($messages)) {
foreach ($messages as $message) {
echo $message, '<br>';
}
}
http://docs.phalconphp.com/en/1.2.6/reference/validation.html
If you are working with models:
<?php
use Phalcon\Mvc\Model\Validator\InclusionIn,
Phalcon\Mvc\Model\Validator\Uniqueness;
class Robots extends \Phalcon\Mvc\Model
{
public function validation()
{
$this->validate(new InclusionIn(
array(
"field" => "type",
"domain" => array("Mechanical", "Virtual")
)
));
$this->validate(new Uniqueness(
array(
"field" => "name",
"message" => "The robot name must be unique"
)
));
return $this->validationHasFailed() != true;
}
}
http://docs.phalconphp.com/en/1.2.6/reference/models.html#validating-data-integrity
models also have events, so you can add any logic you need in these functions:
http://docs.phalconphp.com/en/1.2.6/reference/models.html#events-and-events-manager
I would like to use forms for CRUD as they are very dynamic and reusable.
You can achieve that in forms using options.
You can pass additional options to form and act like a scenario.
You can check Form constructor here
https://docs.phalconphp.com/en/latest/api/Phalcon_Forms_Form.html
In your controller you can pass $options
<?php
use Phalcon\Mvc\Controller;
class PostsController extends Controller
{
public function insertAction()
{
$options = array();
$options['scenario'] = 'insert';
$myForm = new MyForm(null, $options);
if($this->request->hasPost('insert')) {
// this will be our model
$profile = new Profile();
// we will bind model to form to copy all valid data and check validations of forms
if($myForm->isValid($_POST, $profile)) {
$profile->save();
}
else {
echo "<pre/>";print_r($myForm->getMessages());exit();
}
}
}
public function updateAction()
{
$options = array();
$options['scenario'] = 'update';
$myForm = new MyForm(null, $options);
}
}
And your form should look like something this
<?php
// elements
use Phalcon\Forms\Form;
use Phalcon\Forms\Element\Text;
// validators
use Phalcon\Validation\Validator\PresenceOf;
class MyForm extends Form {
public function initialize($entity = null, $options = null) {
$name = new Text('first_name');
$this->add($name);
if($options['scenario'] == 'insert') {
// at the insertion time name is required
$name->addValidator(new PresenceOf(array('message' => 'Name is required.')));
}
else {
// at the update time name is not required
// as well you can add more additional validations
}
}
}
now you can add multiple scenarios and act based on scenarios.

Doctrine_Cache_Apc not works in symfony task

in my doctrine table i got this
public function countHitsFor($object_id) {
return $this->createQuery('s')
->select('COUNT(*) as count')
->where('s.target_id = ?', $object_id);
->useResultCache(true, 3600, 'hits_for_'.$object_id)
->execute(array(), Doctrine_Core::HYDRATE_SINGLE_SCALAR);
}
so if i use this from action its works fine,
first request make sql query,
second load Hits from cache
and what i want is to warm up cache from symfony task
i start task and for each object i call StatTable::getInstance()->countHitsFor($object_id) to create cache data for query, but its not works
after task first request to action makes sql query
UPD
ProjectConfiguration
public function configureDoctrine(Doctrine_Manager $manager)
{
$manager->setAttribute(Doctrine_Core::ATTR_RESULT_CACHE, new Doctrine_Cache_Apc());
}
task
<?php
class warm_up_stat_cacheTask extends sfProgressTask
{
protected function configure()
{
$this->addOptions(array(
new sfCommandOption('application', null, sfCommandOption::PARAMETER_REQUIRED, 'The application name', 'frontend'),
new sfCommandOption('env', null, sfCommandOption::PARAMETER_REQUIRED, 'The environment', 'prod'),
new sfCommandOption('connection', null, sfCommandOption::PARAMETER_REQUIRED, 'The connection name', 'doctrine'),
// add your own options here
));
$this->namespace = 'my_tasks';
$this->name = 'warm_up_stat_cache';
$this->briefDescription = '';
$this->detailedDescription = '';
}
protected function execute($arguments = array(), $options = array())
{
// initialize the database connection
$databaseManager = new sfDatabaseManager($this->configuration);
$connection = $databaseManager->getDatabase($options['connection'])->getConnection();
$contextInstance = sfContext::createInstance($this->configuration);
$Objects = ObjectTable::getInstance()->findAll();
foreach ($Objects as $obj) {
StatTable::getInstance()->countHitsFor($obj->id);
}
$obj->free();
}
}
comand
php symfony my_tasks:warm_up_stat_cache
UPD2
as i understand the problem is in APC
public function countHitsFor($object_id) {
$ckey = 'hits_for_'.$object_id;
if (!apc_exists($ckey))
apc_store($ckey,$this->createQuery('s')
->select('COUNT(*) as count')
->where('s.target_id = ?', $object_id);
->useResultCache(true, 3600, 'hits_for_'.$object_id)
->execute(array(), Doctrine_Core::HYDRATE_SINGLE_SCALAR));
return (int) apc_fetch($ckey);
}
this not works too, may be APC makes prefixes for keys for different environments?
The problem is in Apc, its use separate memory spaces for Apache (mod_php) and for Cli.
I made simple key-value storage on MyISAM table.
Thanks.

How to preserve session through all tests on phpunit?

I'm working on testing a shopping cart, checkout, payment process on Zend Framework with phpunit. I'm testing ShoppingCartController by adding products to cart, a ShoppingCart Model handles product additions by storing product id's in a Zend Session Namespace, and then in another test I want to test that the products were added. The same ShoppingCart Model retrieves a list of added products from the same Zend Session namespace variable.
The add product test looks like this and works well, and the var_dump($_SESSION) was added to debug and shows the products correctly:
public function testCanAddProductsToShoppingCart() {
$testProducts = array(
array(
"product_id" => "1",
"product_quantity" => "5"
),
array(
"product_id" => "1",
"product_quantity" => "3"
),
array(
"product_id" => "2",
"product_quantity" => "1"
)
);
Ecommerce_Model_Shoppingcart::clean();
foreach ($testProducts as $product) {
$this->request->setMethod('POST')
->setPost(array(
'product_id' => $product["product_id"],
'quantity' => $product["product_quantity"]
));
$this->dispatch($this->getRouteUrl("add_to_shopping_cart"));
$this->assertResponseCode('200');
}
$products = Ecommerce_Model_Shoppingcart::getData();
$this->assertTrue($products[2][0]["product"] instanceof Ecommerce_Model_Product);
$this->assertEquals($products[2][0]["quantity"],
"8");
$this->assertTrue($products[2][1]["product"] instanceof Ecommerce_Model_Product);
$this->assertEquals($products[2][1]["quantity"],
"1");
var_dump($_SESSION);
}
The second test attempts to retrieve the products by asking the model to do so, the var_dump($_SESSION) is null already at the beginning of the test. The session variables were reset, I want to find a way to preserve them, can anyone help?
public function testCanDisplayShoppingCartWidget() {
var_dump($_SESSION);
$this->dispatch($this->getRouteUrl("view_shopping_mini_cart"));
$this->assertResponseCode('200');
}
Sorry for pointing you in the wrong direction. Here is a way better way of achieving this, suggested by ashawley from #phpunit channel of irc.freenode.net:
<?php
# running from the cli doesn't set $_SESSION here on phpunit trunk
if ( !isset( $_SESSION ) ) $_SESSION = array( );
class FooTest extends PHPUnit_Framework_TestCase {
protected $backupGlobalsBlacklist = array( '_SESSION' );
public function testOne( ) {
$_SESSION['foo'] = 'bar';
}
public function testTwo( ) {
$this->assertEquals( 'bar', $_SESSION['foo'] );
}
}
?>
== END UPDATE
In function tearDown(): copy $_SESSION to a class attribute and
In function setUp(): copy the class attribute to $_SESSION
For example, this test fails when you remove the functions setUp() and tearDown() methods:
<?php
# Usage: save this to test.php and run phpunit test.php
# running from the cli doesn't set $_SESSION here on phpunit trunk
if ( !isset( $_SESSION ) ) $_SESSION = array( );
class FooTest extends PHPUnit_Framework_TestCase {
public static $shared_session = array( );
public function setUp() {
$_SESSION = FooTest::$shared_session;
}
public function tearDown() {
FooTest::$shared_session = $_SESSION;
}
public function testOne( ) {
$_SESSION['foo'] = 'bar';
}
public function testTwo( ) {
$this->assertEquals( 'bar', $_SESSION['foo'] );
}
}
Also there is a backupGlobals feature but it doesn't work for me. You should try it thought, maybe it works on stable PHPUnit.
that's a very ugly of doing that. The right way should be using dependency injection.
That implies changing your source code to use this class instead of sessions directly:
class Session
{
private $adapter;
public static function init(SessionAdapter $adapter)
{
self::$adapter = $adapter;
}
public static function get($var)
{
return self::$adapter->get($var);
}
public static function set($var, $value)
{
return self::$adapter->set($var, $value);
}
}
interface SessionAdapter
{
public function get($var);
public function set($var, $value);
}
Additional information:
http://community.sitepoint.com/t/phpunit-testing-cookies-and-sessions/36557/2
Using PHPUnit to test cookies and sessions, how?
You can also just create a random session id for your PHPUnit test, and then make sure you pass this session id in a cookie in every further call you make. With Curl, you would use the CURLOPT_COOKIE option and set it to 'PHPSESSID=thesessionidofyourunittest' as such:
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_COOKIE, 'PHPSESSID=thesessionidofyourunittest');
I explained more in detail and with an example in this stackoverflow answer.

Resources