how to use Yiic message command with CDbMessageSource - yii

i used to use CPhpMessageSource but i want to try CDbMessageSource i though i just have to change this...
'components' => array(
'message' => array(
// 'class' => 'CPhpMessageSource',
'class' => 'CDbMessageSource',
),
But when i exectute Yiic message to insert translation data in the database it still generate files in protected/messages/"files".php
...
i must have miss one point...
i follow this http://www.yiiframework.com/doc/api/1.1/CDbMessageSource

Even though Yii has a "reader" for database translations, it doesn't have a "writer". What I usually end up doing is writing some code that can populate the tables using the generated translation files.
You can also write a derivated "message" command for Yii and use that to insert in the database directly, it's not that much work:
<?php
Yii::import('system.cli.commands.MessageCommand', TRUE);
class DbMessageCommand extends MessageCommand
{
protected function generateMessageFile($messages,$fileName,$overwrite,$removeOld,$sort)
{
if (preg_match('#/(..)/([^\\/:"*?<>|]+?)\.php$#i', $fileName, $matches))
{
$language = $matches[1];
$category = $matches[2];
foreach ($messages as $message)
{
// $message contains the string, $category has the category and $language is the current language
// Add to your DB here
}
}
}
}
Just create a DbMessageCommand.php file in your commands subdirectory and finish the code and you should be in business.
In short, what this does is use the regular message command to parse the files, but when the generateMessageFile-function is called to write to the php file, it uses a regexp to determine what would be written (it's called per language/category) and adds it in the database.
I haven't included that code, because I don't use the models as they are included in Yii. I have my own CDbMessageSource-variant and my own string tables.

Related

How to use existing data from the database in Codeception FactoryMuffin?

I'm trying to set up easy test data in my Acceptance tests:
public function shouldUseAFakeAccountHolder(AcceptanceTester $I) {
$I->have(AccountHolder::class);
// ...
}
I've copied the example code from the Codeception documentation and modified it with my entity names (as well as fixing the bugs).
<?php
public function _beforeSuite()
{
$factory = $this->getModule('DataFactory');
// let us get EntityManager from Doctrine
$em = $this->getModule('Doctrine2')->_getEntityManager();
$factory->_define(AccountHolder::class, [
'firstName' => Faker::firstName(),
// Comment out one of the below 'accountRole' lines before running:
// get existing data from the database
'accountRole' => $em->getRepository(AccountRole::class)->find(1),
// create a new row in the database
'accountRole' => 'entity|' . AccountRole::class,
]);
}
The relationship using existing data 'accountRole' => $em->getRepository(AccountRole::class)->find(1) always fails:
[Doctrine\ORM\ORMInvalidArgumentException] A new entity was found through the relationship 'HMRX\CoreBundle\Entity\AccountHolder#accountRole' that was not configured to cascade persist operations for entity: HMRX\CoreBundle\Entity\AccountRole#0000000062481e3f000000009cd58cbd. To solve this issue: Either explicitly call EntityManager#persist() on this unknown entity or configure cascade persist this association in the mapping for example #ManyToOne(..,cascade={"persist"}). If you cannot find out which entity causes the problem implement 'HMRX\CoreBundle\Entity\AccountRole#__toString()' to get a clue.
If I tell it to create a new entry in the related table 'accountRole' => 'entity|' . AccountRole::class, it works, but then it adds rows to the table when it should be using an existing row. All the role types are known beforehand, and a new random role type makes no sense because there's nothing in the code it could match to. Creating a duplicate role works, but again it makes so sense to have a separate role type for each user since roles should be shared by users.
I've had this error before in Unit tests, not Acceptance tests, when not using Faker / FactoryMuffin, and it's been to do with accessing each entity of the relationship with a different instance of EntityManager. As soon as I got both parts using the same instance, it worked. I don't see how to override the native behaviour here though.
It works (at least in Codeception 4.x) by using a callback for the existing relation:
<?php
public function _beforeSuite()
{
$factory = $this->getModule('DataFactory');
$em = $this->getModule('Doctrine2')->_getEntityManager();
$factory->_define(AccountHolder::class, [
'firstName' => Faker::firstName(),
'accountRole' => function($entity) use ($em) {
$em->getReference(AccountRole::class)->find(1);
},
]);
}
I've found it here: https://github.com/Codeception/Codeception/issues/5134#issuecomment-417453633

Can't insert into database with save()

I am having an issue inserting a record into the database. I am a beginner with the Yii framework, so I may have made some stupid mistakes.
This is from the SiteController
public function actionCreatePost(){
$model = new PostForm();
$post = new Post();
if ($model->load(Yii::$app->request->post()) && $model->validate()) {
$post->body = $model->body;
$post->title = $model->title;
$post->save();
return $this->redirect('index');
}else {
return $this->render('createPost', ['model' => $model]);
}
}
This is from the Post class
public function behaviors()
{
return [
[
'class' => TimestampBehavior::className(),
'createdAtAttribute' => 'created_at',
'updatedAtAttribute' => 'updated_at',
'value' => new Expression('NOW()'),
],
[
'class' => BlameableBehavior::className(),
'createdByAttribute' => 'id_author',
]
];
}
The issue is that you have created a PostForm class for the form (which is correct) but you are then trying to load the response into the Post class - a completely different class. This won’t work without modification.
If you have a look at the response:
var_dump(Yii:$app->request->post());
You will see the form data is located within the PostForm key. Yii will therefore only load the data into the PostForm class.
The correct solution is therefore to create a savePost() function within the PostForm eg:
public function savePost(){
$model = new Post();
$model->propertyABC = $this->propertyABC
...etc...
$model->save();
So the action would appear as follows:
$model = new PostForm();
If($model->load(Yii::$app->request->post()) && $model->validate()){
$model->savePost();
The other option is to rename the key from PostForm to Post. Yii will then load the data but this is not the best approach as it is a bit obscure.
Hope that helps
I would guess the issue is with the validation.
I can see several issues I will point out. First, I cannot figure out why are you creating a new PostForm, loading the data in it and verifying it, just to dump some values in a new Post and save it. Are there some functions, you are running in the PostForm model, that are triggered by load or verify? If that is not the case, I would suggest dropping one of the models, and using only the other. Usually, that is the Form model. It serves as a link between the ActiveForm and the model handling everything. You can do everything in the createPost() function in the Form model, and then in the controller it will look like
if ($model->load(Yii::$app->request->post())) {
$model->save();
return $this->redirect('index');
}
Second of all, you can dump post->getErrors() before the save to see if there are any errors with the validation. What you can also do, is call $post->save(false) instead. If you pass false to it, it will not trigger $post->validate(), and some errors can be neglected. Please, let me know if there is anything unclear.

How in codeception to make rollback a database if the test failed?

I need to test the feature in account. But for this need register an account. If the feature does not work correctly and the test fails, how do I can automatically delete an account from a database (account created during testing)?
I think you have a few options.
You can do clean-up in your Cest class's _before or _after methods (if you use a framework you could use an ORM to delete all accounts for example).
Codeception's Db module (see https://codeception.com/docs/modules/Db) also has a cleanup flag which, when true, will load a user-defined database dump before each test (you could create a dump with no accounts).
There might be other options too. If you use Yii2 for example, the Yii2 module for Codeception has a cleanup flag that will wrap tests in a transaction if true (see https://codeception.com/for/yii).
We are facing problems like this, too. If you insert the account with the DB module of codception you can use the cleanup flag and it will automaticly clean up the database after each run.
If you create the account by a test and you want to go sure that the account isn't existing before you start the test you can extend the DB module by a delete function (that has to used with care, we only allow that in testing environments).
<?php
namespace Helper\Shared;
class DbHelper extends \Codeception\Module {
public function deleteFromDatabase($table, $criteria)
{
$dbh = $this->getModule('Db')->_getDbh();
$query = "delete from `%s` where %s";
$params = [];
foreach ($criteria as $x => $y) {
$params[] = "`$x` = '$y'";
}
$params = implode(' AND ', $params);
$query = sprintf($query, $table, $params);
codecept_debug($query);
$this->debugSection('Query', $query, json_encode($criteria));
$sth = $dbh->prepare($query);
return $sth->execute(array_values($criteria));
}
}
This can be used in the test code with ...
$I->deleteFromDatabase('account', ['id' => '123456']);
If it's possible the DB Module should be used to create the account and clean it up again. This method above is pretty dangerous dependend on the systems you are using it.

Zend Authentication 'Zend\Authentication\Adapter\DbTable' gives error while authenticating

I used a simple instruction from
http://framework.zend.com/manual/2.0/en/modules/zend.authentication.adapter.dbtable.html#advanced-usage-by-example for Zend Authentication.
Here is my code:
$adapter = $sm->get('adapter');
$authAdapter = new DbTable($adapter);
$authAdapter -> setTableName('users')->setIdentityColumn('username')->setCredentialColumn('password');
$authAdapter -> setIdentity('admin')-> setCredential('password');
$authAdapter -> authenticate();
The above code generates error as follows:
The supplied parameters to DbTable failed to produce a valid sql statement, please check table and column names for validity.
I know if makes sense to use ZF-Commons and ZF-Users module and not reinvent the wheel... but being relatively new to ZF2 I want to try it myself.
The answer's right there in the error message, Zend\Authentication\Adapter\DbTable expects more than just an adapter as a parameter, it also needs a table name, and the name of the identifier and credential columns...
$authAdapter = new DbTable($dbAdapter,
'tableName',
'identifierColumnName',
'credentialColumnName'
);
This info is covered in the docs, which is always a good starting point -> http://zf2.readthedocs.org/en/release-2.1.4/modules/zend.authentication.adapter.dbtable.html
I know this is an old one and probably you have the answer now. Even though, I will put my answer here for further users. I myself faced lots of similar issues where the tutorials expect us to have some preconditions in place.
This error occur because you are not completely right in the statement to get the DB adapter. You need to call a service management instance with an string defined in your locals or globals configurations. For example:
I have this factory defined in my global.php file:
return array(
'service_manager' => array(
'factories' => array(
'Zend\Db\Adapter\Adapter'
=> 'Zend\Db\Adapter\AdapterServiceFactory',
),
),
'db' => array(
'driver' => 'Pdo',
'driver_options' => array(
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''
),
),
);
In my local.php I have the credentials and information to reach my DB server as follow:
return array(
'db' => array(
'username' => 'valid_dbusername',
'password' => 'valid_dbpass',
'dsn' => 'mysql:dbname=valid_dbname;host=valid_dbserver',
),
);
This is enough to define my service calle 'Zend\Db\Adapter\Adapter'. Then, to instantiate my Db adapter I have the following lines inside any function in my controller:
if (!$this->adapter) {
$sm = $this->getServiceLocator();
$this->adapter = $sm->get('Zend\Db\Adapter\Adapter');
}
It is important to note adapter here is a class variable. So, it must be defined inside my controller class like:
public $adapter;
This steps are enough to you get an DB adapter. I am assuming you dont have a factory called 'adapter'. This should make your example work.

zend_db standalone

i want to use zend_db standalone cos zend framework is too much for my project but i'm new with it,
is it correct to do this:
$pdoParams = array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES
UTF8;');
$params = array(
'host' => 'localhost',
'username' => 'ss_fraat',
'password' => 'jos10',
'dbname' => '_a2hDB',
'driver_options' => $pdoParams
);
try {
$db = Zend_Db::factory('PDO_MYSQL', $params);
//set default adapter
Zend_Db_Table_Abstract::setDefaultAdapter($db);
} catch (Exception $e) {
exit($e->getMessage());
}
//save Db in registry for later use
Zend_Registry::set('dbAdapter', $db);
then in any class do this:
$db = Zend_Registry::get('db');
/** quote to avoid sql injection */
$date = $db->quote('1980-01-01');
$sql = 'SELECT * FROM product WHERE name = ' . $date;
$result = $db->query($sql);
$db->query(); //run a query
i really need to do this
Zend_Db_Table_Abstract::setDefaultAdapter($db);
i get this code from a website,
is it necessary to use Zend_Db_Table_Abstract if i'm not using the full zend framework,
or it is better for example to use this:
$db = Zend_Db::factory( ...options... );
$select = new Zend_Db_Select($db);
what i want is to setup a pdo/mysql connexion in my bootstrap php page and be able to get that db instance in any class without starting a new connexion to execute queries but i'm not sure how to do that use Zend_Db_Table_Abstract or Zend_Db_Select use the registry Zend_Registry::set('dbAdapter', $db) or not
thanks a lot
The purpose of Zend_Db_Table_Abstract is so you can create your own model classes based around the Table Data Gateway design pattern. The idea of that pattern is that you have a class that encapsulates all the sql you would need for interfacing with a table. So the assumption is that you will be creating model classes that extend Zend_Db_Table_Abstract for each table. If you are going to do that, then you will want to call Zend_Db_Table_Abstract::setDefaultAdapter($db) in your setup/bootstrap. Recent versions of ZF provide as an alternative a quick way of getting basic functionality without having to create a custom class definition by just instantiating Zend_Db_Table:
$userTable = new Zend_Db_Table('users');
In summary, none of this particularly has to do with the MVC part of the framework, although some people choose to use Zend_db as the basis for db connections and models, instead of using a more fully featured ORM like Doctrine or Propel.
The other code you provided simply illustrates that you do not need to use Zend_Db_Table_Abstract either -- you can simply setup an instance of a Zend_Db_Adapter and use that instance to call query() or its other methods.