I am using Symfony 2.7, doctrine 2, MySQL.
I'm trying to retrieve the tables creation order in a controller as when issuing
php app/console doctrine:generate:schema --dump-sql
but I only need the table names.
So, for instance, if I have two tables like
Product - Category I'd like to have an output which looks like this : array('Category', 'Product')
Using this documentation here's what I've done so far:
public function getTablesCreationOrderAction()
{
$conn = $this->get('database_connection');
$sm = $conn->getSchemaManager();
$sequences = $sm->listSequences($this->getParameter('database_name'));
die();
}
An exception is thrown at $sequences = $sm->listSequences.
Here's the exception I get:
Operation 'Doctrine\DBAL\Platforms\AbstractPlatform::getListSequencesSQL' is not supported by platform.
I don't know if this means that MySQL does not support the operation.
Thanks!
Yes, but it seems like the feature isn't implemented yet, since this is in the source code of AbstractPlatform you will see this:
public function getListSequencesSQL($database)
{
throw DBALException::notSupported(__METHOD__);
}
Now this means that platforms overriding AbstractPlatform must override these functions to provide the right implementation.
Now if you take a look at MySqlPlatform, you won't be able to find the getListSequencesSQL() anywhere, hence you can't use this feature with mysql or at least it's not implemented.
For those interested, here's how I did it. There may be better ways..
public function getTablesCreationOrderAction()
{
$app = New Application($this->get('kernel'));
$app->setAutoExit(false);
$input = New ArrayInput(['command' => 'doctrine:schema:create',
'--dump-sql'=>true]);
$output = New BufferedOutput();
$app->run($input, $output);
$rst = $output->fetch();
$schema = explode('CREATE TABLE ', $rst);
$tables = [];
foreach ($schema as $tableDef)
{
$tables[] = explode(' (', $tableDef)[0];
}
unset($tables[0]); // empty
var_dump($tables);
die();
}
Related
Good afternoon,
I have a bunch of legacy code that uses the old mysql library (mysql_query($sql) for example) and am trying to test it with PHPUnit (4.8 is the latest that will run on the server for various reasons).
Does anyone know how to mock this database connection so that it can return predetermined results when predetermined queries are run? I have tried using getConnection() (as per the docs here: http://devdocs.io/phpunit~4/database) to no avail.
For example I have this class:
class Raffle {
...
public static function loadAll($filter=""){
//$filter = mysql_real_escape_string($filter); // protect against SQL injection
$raffles = []; // the array to return
$sql = "SELECT * FROM raffles $filter;"; // include the user filter in the query
$query = mysql_query($sql);
//echo mysql_error();
while($row = mysql_fetch_assoc($query)){
$raffles[] = Raffle::loadFromRow($row); // generate the raffe instance and add it to the array
}
return $raffles; // return the array
}
...
}
(The mysql_connect() call is done in a file called db.php which is loaded on every page that it is needed and not in the class file itself.)
Thanks in advance.
For anyone else stuck in this situation I found that building a mock for PDO that contains a fallback to the functional api allowed me to migrate the code to a Testable OO based model while still using the old mysql library under the hood.
For example:
// the basic interfaces (function names taken from PDO
interface DBConnAdapter {
public function query($sql);
}
// since PDO splits connections and statements the adapter should do the same
interface DBQueryAdapter {
public function num_rows();
public function fetch_assoc();
}
...
class DBAdapter implements DBConnAdapter {
public function query($sql){
$query = mysql_query($sql); // run the query using the legacy api
return $query ? new QueryAdapter($query) : false; // return a new query adapter or false.
}
}
...
// an example of a basic mock object to test sql queries being sent to the server (inspired by mockjax :-) )
class DBMock implements DBConnAdapter {
public $queries = []; // array of queries already executed
public $results = []; // array of precomputed results. (key is SQL and value is the returned result (nested array))
public function query($sql) {
if($this->results[$sql]){
$query = new DBQueryMock($sql, $this->results[$sql]); // a mock of PDOStatement that takes the sql it ran and the results to return
$queries[] = $query; // add the query to the array
return $query; // return the query
}
return false; // we do not know the statement so lets pretend it failed
}
// add a result to the list
public function add_single_result($sql, $result){
// check if the index was set, if not make an array there
if(!isset($this->results[$sql])) $this->results[$sql] = [];
// add the result to the end of the array
$this->results[$sql][] = $result;
// return its index
return count($this->results[$sql]) - 1;
}
}
Admittedly this is not an ideal solution as it requires modifying the code to support the adapter objects and removes some functionality (such as mysql_real_escape_string), but it worked....
If you have a better solution please share :-) Thanks!
I've a problem using elasticsearch in a php application. The application is built with zend and uses a .env to hold the following configuration:
ELASTICSEARCH_MAX_DOCUMENTS=250
ELASTICSEARCH_MAX_BULK_SIZE=3M
ELASTICSEARCH_HOST=my-elasticsearch.intern.rz
ELASTICSEARCH_PORT=80
ELASTICSEARCH_USER=user
ELASTICSEARCH_PASSWORD=pw
The call to index the new files is part of a import service class and looks like this:
public function flush(FlushInterface $flushInterface = null) {
$bulk = $this->getSearchDocumentBulk();
if (!empty($bulk->getActions())) {
$response = $bulk->send();
$this->resetSearchDocumentBulk();
if (0 === $response->count()) {
$data = $response->getData();
throw new BulkException(isset($data['message']) ? strip_tags($data['message']) : '');
}
}
$this->documentCache = [];
if ($flushInterface instanceof FlushInterface) {
$flushInterface->flush();
}
return $this;
}
protected function getSearchDocumentBulk() {
if (!($this->searchDocumentBulk instanceof Bulk)) {
$this->searchDocumentBulk = new Bulk($this->getSearchClient()->getClient());
$this->searchDocumentBulk->setIndex(self::INDEX);
}
return $this->searchDocumentBulk;
}
I know it's only a short snippet but it's quite difficult to pull out the relevant code. So please let me know if I have to post some more methods.
The application is started by a symfony command and I'm able to curl to elasticsearch (version 5.1) without any errors.
My problem is that no document is indexed. If I check elasticsearch-head I see that there was no data transfer anymore.
But there's also no error, no exception or something like that. The process is completed with 100% (100,000 of 100,000 files imported). So I've no idea what happens or how to find out the bug.
i am trying to insert new entity using PHP client library into datastore, i am using datastore_connect.php file from this example, https://github.com/amygdala/appengine_php_datastore_example
I want to insert entity with auto id, not the name. I see that there is function setId(), but i dont know how to generate proper id. Whats the best practice in doing so?
Thanks
function createKeyForTestItem () {
$path = new Google_Service_Datastore_KeyPathElement();
$path->setKind("testkind");
$path->setName("testkeyname");
//$path->setId(??)
$key = new Google_Service_Datastore_Key();
$key->setPath([$path]);
return $key;
}
You can have Cloud Datastore generate the ID for you by populating the insertAutoId field on the mutation instead of the upsert field.
Here's a code snippet (adapted from the datastore_connect.php file you posted):
function create_key() {
$path = new Google_Service_Datastore_KeyPathElement();
$path->setKind("testkind");
// Neither name nor ID is set.
$key = new Google_Service_Datastore_Key();
$key->setPath([$path]);
return $key;
}
function create_entity() {
$entity = new Google_Service_Datastore_Entity();
$entity->setKey(create_key());
// Add properties...
return $entity;
}
function create_commit_request() {
$entity = create_entity();
$mutation = new Google_Service_Datastore_Mutation();
$mutation->setInsertAutoId([$entity]); // Causes ID to be allocated.
$req = new Google_Service_Datastore_CommitRequest();
$req->setMode('NON_TRANSACTIONAL');
$req->setMutation($mutation);
return $req;
}
If you're looking for a PHP library to take away most of the headache of Cloud Datastore, you could try my new library, which sits on top of the official google-api-php-client:
https://github.com/tomwalder/php-gds
And here's a sample code snippet to create an Entity with an auto-generated ID
$obj_book = new GDS\Entity();
$obj_book->title = 'Romeo and Juliet';
$obj_book->author = 'William Shakespeare';
$obj_book->isbn = '1840224339';
// Write it to Datastore
$obj_book_store->upsert($obj_book);
More code snippets and documentation on GitHub.
What is the best way to write a unit test for a class which depends on an Eloquent model with relationships? E.g.
real object (with database). This is easy, but slow.
real object (no database). I can create a new object but I can't see how to set the related models without writing to the database.
mock object. I run into issues using Mockery with Eloquent models (e.g. see this question).
other solutions?
context: I'm using Laravel with Authority RBAC for access control. I want to find the best way to test my access rules in a unit test. Which means I need to pass the user dependencies to Authority during the test.
If you're writing unit tests, you shouldn't ever use a database. Testing against a database would be considered an integration test. Check out Roy Osherove's videos.
To answer your question, (and not having delved into Authority RBAC, I'd do something like this:
// assuming some RBAC class
SomeRBACClass extends RBACBaseClass {
function validate(UserClass $user) {
if (!$roles = $user->getRoles())
{
return false;
}
$allowed = array('admin', 'superadmin');
foreach ($roles as $role) {
if (in_array($role->name, $allowed)) {
return true;
}
}
return false;
}
}
SomeRBACClassTest extends TestCase {
function test_validate_WhenPassedUser_callsGetRolesOnUserWithNoArgs()
{
$rbac = new SomeRBACClass();
$user = Mockery::mock('UserClass');
$user->shouldReceive('getRoles')->once()->withNoArgs();
$rbac->validate($user);
}
function test_validate_getRolesOnUserReturnsCollectionOfRoles_CallsGetAttributeWithNameOnFirstRole() {
$rbac = new SomeRBACClass();
$user = Mockery::mock('UserClass');
// assuming $user->getRoles() returns a collection
$collection = new \Illuminate\Support\Collection(array(
$role1 = Mockery::mock('Role'),
$role2 = Mockery::mock('Role'),
));
$user->shouldReceive('getRoles')->andReturn($collection);
$role1->shouldReceive('getAttribute')->once()->with('name');
$rbac->validate($user);
}
function test_validate_getAttributeWithNameOnRoleReturnsValidRole_ReturnsTrue() {
$rbac = new SomeRBACClass();
$user = Mockery::mock('UserClass');
// assuming $user->getRoles() returns a collection
$collection = new \Illuminate\Support\Collection(array(
$role1 = Mockery::mock('Role'),
$role2 = Mockery::mock('Role'),
));
$user->shouldReceive('getRoles')->andReturn($collection);
$role1->shouldReceive('getAttribute')->andReturn('admin');
$result = $rbac->validate($user);
$this->assertTrue($result);
}
This is not a thorough example of all the unit tests that I would write, but it's a start. E.g., I would also validate that when no roles are returned, that the result is false.
my Code
function db_fetch_array($query_id = '')
{
if(!$query_id)
{
$query_id = $this->query_result;
}
if($query_id)
{
// success
$this->row[$query_id] = mysql_fetch_array($query_id);
return $this->row[$query_id];
}
else
{
// failure
return false;
}
}
for starters i would do something like this:
if (empty($query_id))
instead of
if (!$query_id)
since !$query_id does not check for empty strings
Further, the error seems to be an issue with using the $query_id as an array key. You may need to find a different identifier
as per divaka's potential alternative in comments below:
Use something else for a key. You can fetch the result first, then get the id of the row and apply it as a key in you array
In case you want to stack several database connections,
consider linking the database in the constructor and instanciate several of these classes, which you can hold in an array.
Example:
class Database
{
protected $link;
public function __construct($user, $pass, $host) {
$this->link = new Mysqli(/* ... */);
}
public function fetch($query) {
$stmt = $this->link->prepare($query);
/* ... */
}
/* ... */
}
Now have your databases in an array:
$db[0] = new Database('root', 'root', 'server1');
$db[1] = new Database('root', 'root', 'server2');
$db['bigCluster'] = new Database('user01', 'passwd', 'cluster.your.biz');
$result = $db[0]->fetch("SELECT * FROM table");
This way you have way better access, control and debugging is easier.
To say something about your code:
function db_fetch_array($query_id = '') { /* .... */ }
This function accepts an empty parameter, thus, allowing the user to fetch from a non-existent database connection. Which results in an error. Don't allow it!
Even better, typehint it:
public function db_fetch_array(\Mysqli $query_id) { /* .... */ }
Solves all your problems at once!
If you need an it your way tho, try the following, giving you a unique, printable id to use as an array key:
$query_id = md5((string)$query_id);
Have a look at the Zend code style guide and modern PHP design patterns, this will help you to increase your code quality by a few hundred percent.