I'm writing PHPUnit tests to test MySQL database filling. My tests work fine locally, but are failing on Travis CI with the following error:
PHPUnit_Extensions_Database_Operation_Exception: COMPOSITE[TRUNCATE] operation failed on query:
TRUNCATE `simple_table`
using args: Array
[SQLSTATE[42S02]: Base table or view not found: 1146 Table 'test.simple_table' doesn't exist]
I have a DatabaseTestCase parent class and a FillCommandTest testing class.
DatabaseTestCase
class DatabaseTestCase extends PHPUnit_Extensions_Database_TestCase
{
private static $pdo = null;
private $connection = null;
/**
* Construct.
*/
public function __construct()
{
$env = new Environment();
$this->dsn = "mysql:dbname=". $env->get('DB_DATABASE', 'test') . ";host=" . $env->get('DB_HOST', '127.0.0.1');
$this->username = $env->get('DB_USERNAME', 'travis');
$this->password = $env->get('DB_PASSWORD', '');
$this->db_name = $env->get('DB_DATABASE', 'test');
}
/**
* Get database connection.
*
* #return PHPUnit_Extensions_Database_DB_DefaultDatabaseConnection
*/
final public function getConnection()
{
if ($this->connection === null) {
if (self::$pdo == null) {
self::$pdo = new PDO($this->dsn, $this->username, $this->password);
}
$this->connection = $this->createDefaultDBConnection(self::$pdo, $this->db_name);
}
return $this->connection;
}
/**
* Get XML dataset.
*
* #param string $file
* #return string
*/
public function getDataSet($file = 'empty.xml')
{
$file = 'tests/datasets/' . $file;
return $this->createXmlDataSet($file);
}
/**
* Set up the test database table
*
* #param PHPUnit_Extensions_Database_DB_DefaultDatabaseConnection $connection
*/
protected function setUpTable($connection)
{
$pdo = $connection->getConnection();
$sql = "CREATE TABLE IF NOT EXISTS simple_table (
id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
email VARCHAR(50) NOT NULL,
address VARCHAR(100)
)";
$pdo->exec($sql);
}
}
FillCommandTest
class FillCommandTest extends DatabaseTestCase
{
protected $app;
protected $command;
protected $command_tester;
/**
*
*/
public function setUp()
{
$this->app = new Application();
$this->app->add(new FillCommand());
$this->command = $this->app->find('fill');
$this->command_tester = new CommandTester($this->command);
parent::setUp();
$connection = $this->getConnection();
$this->setUpTable($connection);
}
/**
*
*/
public function test_parse_and_fill_simple_table()
{
copy(getcwd() . '/tests/files/FillSimpleTable.php', getcwd() . '/src/Fillers/FillSimpleTable.php');
copy(getcwd() . '/tests/files/simple.txt', getcwd() . '/src/Files/simple.txt');
$this->command_tester->execute(array(
'command' => $this->command->getName(),
'name' => 'simple_table'
));
$actual = $this->getConnection()->createQueryTable('simple_table', 'SELECT * FROM simple_table');
$expected = $this->getDataSet('simple_table.xml')->getTable('simple_table');
$this->assertTablesEqual($expected, $actual);
unlink(getcwd() . '/src/Fillers/FillSimpleTable.php');
unlink(getcwd() . '/src/Files/simple.txt');
}
}
What am I doing wrong?
By the looks of it, it seems your code is referring to a database called 'test'.
But I am assuming since you are deploying, your database name should be different. Make sure your .env DB_DATABASE variable reflects the correct one on your production server.
Related
I'm super new to PHP mysql database, can someone help me out how can I make my input appear on the database. This is just a sample I'm testing. I don't know what I'm doing wrong I have all the information from the server inputed it at the right place. I'm getting a messages that it was successful but nothing is going to my database. I have one file called connect.php where the php is and the index.php for the html, table is name cultivar_db. Thanks in advance.
NOTE: Im connecting to my real server.
Structure
1 cultivar_idPrimary int(11) No None AUTO_INCREMENT
2 cultivar_name varchar(20) latin1_swedish_ci No None
Heres the complete details in the database
<?php
$servername = "localhost";
$username = "myusername";
$password = "*****";
$dbname = "mydbname";
$cultivar_name = $_POST['cultivar_name'];
//Database Connection
$conn = new mysqli($servername, $username, $password, $dbname);
if($conn->connect_error){
die('Connection Failed : ' . $conn->connect_error);
}else{
$stmt = $conn->prepare("INSERT INTO cultivar_db(cultivar_name)
values(?)");
$stmt->bind_param("s", $cultivar_name);
$stmt->execute();
echo "successfully input datas..";
$stmt->close();
$conn->close();
}
?>
<div class="box">
<form action="connect.php" method="post">
<label>Cultivar Name:</label>
<input class="inputstyle" type="text" name="cultivar_name"><br />
<input class="button inputstyle" type="submit" value="import" >
</form>
</div>
Three possible issues within
$stmt = $conn->prepare("INSERT INTO cultivar_db(cuttivar_id, cultivar_name) values(?,?,)");
The second , within the values might causes some trouble
Your ID field is set to AUTO INCREMENT, therefor no reason to insert it
You typed cuttivar_id in your SQL, instead of cultivar_id
If you created your table like this
CREATE TABLE cultivar_db (
cultivar_id NOT NULL AUTO_INCREMENT,
cultivar_name VARCHAR(20),
PRIMARY KEY (cultivar_id)
);
Then all you need for the insert is
$stmt = $conn->prepare("INSERT INTO cultivar_db(cultivar_name)
values(?)");
$stmt->bind_param('s', $cultivar_name);
$stmt->execute();
If your "cultivar_id" is auto-increment, you do not need to add that id from the HTML. You can simply pass the "cultivar_name" from HTML using $_POST.
$stmt = $conn->prepare("INSERT INTO cultivar_db(cultivar_name) values(?)");
$stmt->bind_param('s', $_POST['cultivar_name']);
$stmt->execute();
ok I'm a little late; but I hope to be still opportune; I won't add anything regarding syntax errors that other users have already told you to fix;
In my case I will leave you a working example of a database connection system and insert, each property and method has its PHPDOCS where a detailed explanation of the purpose or responsibility of the method is added; You will also notice that I have implemented the getter and setter of each private property:
db.php (file class)
<?php
class HandlerDataBase
{
/**
* #var string
* store the host server point
*/
private string $host;
/**
* #var string
* store database name
*/
private string $db;
/**
* #var string
* store the port connection point
*/
private string $port;
/**
* #var string
* store de username to implement in the connection to db server
*/
private string $user;
/**
* #var string
* password
*/
private string $password;
/**
* #param array $config
* #return void
* this method is implement to set the configuration to the database connection
*/
public function setConfigConnection(array $config)
{
foreach ($config as $key => $value) {
switch ($key) {
case 'host':
$this->setHost($value);
break;
case 'db':
$this->setDb($value);
break;
case 'port':
$this->setPort($value);
break;
case 'user':
$this->setUser($value);
break;
case 'password':
$this->setPassword($value);
break;
}
}
}
/**
* #return string
*/
public function getHost(): string
{
return $this->host;
}
/**
* #param string $host
*/
public function setHost(string $host): void
{
$this->host = $host;
}
/**
* #return string
*/
public function getDb(): string
{
return $this->db;
}
/**
* #param string $db
*/
public function setDb(string $db): void
{
$this->db = $db;
}
/**
* #return string
*/
public function getPort(): string
{
return $this->port;
}
/**
* #param string $port
*/
public function setPort(string $port): void
{
$this->port = $port;
}
/**
* #return string
*/
public function getUser(): string
{
return $this->user;
}
/**
* #param string $user
*/
public function setUser(string $user): void
{
$this->user = $user;
}
/**
* #return string
*/
public function getPassword(): string
{
return $this->password;
}
/**
* #param string $password
*/
public function setPassword(string $password): void
{
$this->password = $password;
}
/**
* #param string $query
* #param $value
* #return void
* this method implements a connection and executes the query script and the value passed.
* if there is an error it will be displayed, and execution will be stopped.
*/
public function setData(string $query, $value)
{
try {
$conn = $this->getConnection();
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$conn->beginTransaction();
$conn->exec("set names utf8");
$stmt = $conn->prepare($query);
$stmt->bindValue(1, $value);
$stmt->execute();
$conn->commit();
$conn->close();
} catch (PDOException|Exception $e) {
var_dump($e);
}
}
/**
* #return PDO
* this method create a connection to the db
*/
private function getConnection(): PDO
{
return new PDO("mysql:host=".$this->getDb().";port=".$this->getPort()." dbname=".$this->getDb(), $this->getUser(), $this->getPassword());
}
}
implementation:
<?php
require_once 'db.php';
$config = [
'host' => 'localhost',
'db' => 'my_db_name',
'port' => '3306',
'user' => 'my_username',
'pass' => '********',
];
if (!isset($_POST['cultivar_name']) || '' != $_POST['cultivar_name']) {
echo 'Error: not received cultivar name data';
exit();
}
$cultivar_name = $_POST['cultivar_name'];
$query = "INSERT INTO cultivar_db(cultivar_name) values(?)";
$handler_database = new HandlerDataBase();
$handler_database->setConfigConnection($config);
$handler_database->setData($query, $cultivar_name);
another way to handle the configuration to the connection to the database, can be with a specific external file, see this:
config.php
<?php
$config = [
'host' => 'localhost',
'db' => 'my_db_name',
'port' => '3306',
'user' => 'my_username',
'pass' => '********',
];
then your implementation would look like this:
<?php
require_once 'config.php';
require_once 'db.php';
if (!isset($_POST['cultivar_name']) || '' != $_POST['cultivar_name']) {
echo 'Error: not received cultivar name data';
exit();
}
$cultivar_name = $_POST['cultivar_name'];
$query = "INSERT INTO cultivar_db(cultivar_name) values(?)";
$handler_database = new HandlerDataBase();
$handler_database->setConfigConnection($config);
$handler_database->setData($query, $cultivar_name);
I created this code where I create an instance of the database and work with it. Now I'm trying to convert the code to a static form, but I can't.
$pdo = new PDO('sqlite:src/chinook.db');
$sql = "CREATE TABLE IF NOT EXISTS uzivatele(
uzivatelId INTEGER PRIMARY KEY,
jmeno TEXT,
prijmeni TEXT,
body INTEGER
);";
$statement = $pdo->prepare($sql);
$statement->execute();
function dropTable($pdo,$name)
{
$sql = "DROP TABLE $name";
$statement = $pdo->prepare($sql);
$statement->execute();
}
...
static
This is how I have a class implemented for pdo (according to the manual) and I would like to implement static methods, such as createTable, but I can't redo it
class Db
{
protected static $pdo = null;
public static function get(): \PDO
{
return self::$pdo ?? (self::$pdo = new \PDO(
'sqlite:hw-06.db',
null,
null,
[
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION
]
));
}
}
use App\Db;
class Account
{
...
public static function createTable(): void
{
$db = Db::get();
$sql = "CREATE TABLE IF NOT EXISTS uzivatele(
uzivatelId INTEGER PRIMARY KEY,
jmeno TEXT,
prijmeni TEXT,
body INTEGER
);";
$statement = $db->prepare($sql);
$statement->execute();
}
index.php
Account::createTable();
If u want to implement a simple singleton, u can use the "getInstance()" concept and combine with "__callStatic" and "call_user_func_array" to make a PDO functions to be static too, all PDO and Database class functions will become static:
<?php
declare(strict_types = 1);
/*
* PDO database class - only one connection alowed
*/
final class Database
{
/**
* #var PDO $connection The connection
*/
private $connection;
/**
* #var Database $instance The single instance
*/
private static $instance;
/**
* #var string $engine The engine of connection
*/
private $engine = 'sqlite:persistence.db'; // sqlite::memory:
/**
* #var array $options Default option to PDO connection
*/
private $options = [
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_OBJ,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => false
];
/**
* Private constructor to prevent instance
*
* #throws \Throwable
* #return void
*/
private function __construct()
{
try {
$this->connection = new PDO($this->engine, null, null, $this->options);
}
catch (\Throwable $error) {
error_log("{$error->getMessage()}");
}
}
/**
* Get an instance of the Database
*
* #return PDO
*/
private static function getInstance(): PDO
{
// If no instance then make one
if (!self::$instance) {
self::$instance = new self;
}
return self::$instance->connection;
}
/**
* Transpiler of static methods for PDOStatements
*
* #var string $method The PDO static method
* #var array $args
* #return string|PDOStatement
*/
public static function __callStatic(string $method, array $args)
{
return call_user_func_array(array(self::getInstance(), $method), $args);
}
/**
* Destroying PDO connection
*
* #return void
*/
public function __destruct()
{
if (!empty($this->connection)) {
unset($this->connection);
}
}
/**
* Magic method clone is empty to prevent duplication of connection
*/
public function __clone() { }
public function __wakeup() { }
public function __toString() { }
}
to use there:
<?php
require_once __DIR__ . '/Database.php';
Database::exec('CREATE TABLE IF NOT EXISTS uzivatele (
uzivatelId INTEGER PRIMARY KEY,
jmeno TEXT,
prijmeni TEXT,
body INTEGER
);');
Database::exec("INSERT INTO uzivatele (jmeno, prijmeni, body) VALUES ('test', 'test', 1);");
var_dump(Database::lastInsertId());
$stmt = Database::prepare("SELECT * FROM uzivatele;");
$stmt->execute();
$data = $stmt->fetchAll();
var_dump($data);
note that "prepared statments objects" are still like objects!
i dont see any problem in using database connections as static, if they are not used in parallel, there is no problem, it even reduces the overhead of creating many connections with the database. but be careful, in some cases it may not be beneficial, as in cases where the code is not being executed by a CGI or FastCGI but by a wrapper, it can cause slowdowns and even give a problem!
Hopefully i am asking this on the right stack exchange forum. If not please do let me know and I will ask somewhere else. I have also asked on Code Review, but the community seems a lot less active.
As I have self learned PHP and all programming in general, I have only recently found out about 'Data Mappers' which allows data to be passed into classes without said classes knowing where the data comes from. I have read some of the positives of using mappers and why they make it 'easier' to perform upgrades later down the line, however I am really struggling to find out the reccomended way of using mappers and their layouts in a directory structure.
Let's assume we have a simple application whos purpose is to echo out a first name and last name of a user.
The way I have been using/creating mappers (as well as the file structure is as follows):
index.php
include 'classes/usermapper.php';
include 'classes/user.php';
$user = new User;
$userMapper = new userMapper;
try {
$user->setData([
$userMapper->fetchData([
'username'=>'peter1'
])
]);
} catch (Exception $e) {
die('Error occurred');
}
if ($user->hasData()) {
echo $user->fullName();
}
classes/user.php
class User {
private $_data;
public function __construct() { }
public function setData($userObject = null) {
if (!$userObject) { throw new InvalidArgumentException('No Data Set'); }
$this->_data = $dataObject;
}
public function hasData() {
return (!$this->_data) ? false : true;
}
public function fullName() {
return ucwords($this->_data->firstname.' '.$this->_data->lastname);
}
}
classes/usermapper.php
class userMapper {
private $_db;
public function __construct() { $this->_db = DB::getInstance(); }
public function fetchData($where = null) {
if (!is_array($where)) {
throw new InvalidArgumentException('Invalid Params Supplied');
}
$toFill = null;
foreach($where as $argument=>$value) {
$toFill .= $argument.' = '.$value AND ;
}
$query = sprintf("SELECT * FROM `users` WHERE %s ", substr(rtrim($toFill), 0, -3));
$result = $this->_db->query($query); //assume this is just a call to a database which returns the results of the query
return $result;
}
}
With understanding that the users table contains a username, first name and last name, and also that a lot of sanitizing checks are missing, why are mappers convenient to use?
This is a very long winded way in getting data, and assuming that users aren't everything, but instead orders, payments, tickets, companies and more all have their corresponding mappers, it seems a waste not to create just one mapper and implement it everywhere in each class.
This allows the folder structure to look a whole lot nicer and also means that code isn't repeated as often.
The example mappers looks the same in every case bar the table the data is being pulled from.
Therefore my question is. Is this how data mappers under the 'domain model mappers' should look like, and if not how could my code be improved? Secondly is this model needed in all cases of needing to pull data from a database, regardless of the size of class, or should this model only be used where the user.php class in this case is very large?
Thank you in advance for all help.
The Data Mapper completely separates the domain objects from the persistent storage (database) and provides methods that are specific to domain-level operations. Use it to transfer data from the domain to the database and vice versa. Within a method, a database query is usually executed and the result is then mapped (hydrated) to a domain object or a list of domain objects.
Example:
The base class: Mapper.php
abstract class Mapper
{
protected $db;
public function __construct(PDO $db)
{
$this->db = $db;
}
}
The file: BookMapper.php
class BookMapper extends Mapper
{
public function findAll(): array
{
$sql = "SELECT id, title, price, book_category_id FROM books;";
$statement = $this->db->query($sql);
$items = [];
while ($row = $statement->fetch()) {
$items[] = new BookEntity($row);
}
return $items;
}
public function findByBookCategoryId(int $bookCategoryId): array
{
$sql = "SELECT id, title, price, book_category_id
FROM books
WHERE book_category_id = :book_category_id;";
$statement = $this->db->prepare($sql);
$statement->execute(["book_category_id" => $bookCategoryId]);
$items = [];
while ($row = $statement->fetch()) {
$items[] = new BookEntity($row);
}
return $items;
}
/**
* Get one Book by its ID
*
* #param int $bookId The ID of the book
* #return BookEntity The book
* #throws RuntimeException
*/
public function getById(int $bookId): BookEntity
{
$sql = "SELECT id, title, price, book_category_id FROM books
WHERE id = :id;";
$statement = $this->db->prepare($sql);
if (!$result = $statement->execute(["id" => $bookId])) {
throw new DomainException(sprintf('Book-ID not found: %s', $bookId));
}
return new BookEntity($statement->fetch());
}
public function insert(BookEntity $book): int
{
$sql = "INSERT INTO books SET title=:title, price=:price, book_category_id=:book_category_id";
$statement = $this->db->prepare($sql);
$result = $statement->execute([
'title' => $book->getTitle(),
'price' => $book->getPrice(),
'book_category_id' => $book->getBookCategoryId(),
]);
if (!$result) {
throw new RuntimeException('Could not save record');
}
return (int)$this->db->lastInsertId();
}
}
The file: BookEntity.php
class BookEntity
{
/** #var int|null */
protected $id;
/** #var string|null */
protected $title;
/** #var float|null */
protected $price;
/** #var int|null */
protected $bookCategoryId;
/**
* Accept an array of data matching properties of this class
* and create the class
*
* #param array|null $data The data to use to create
*/
public function __construct(array $data = null)
{
// Hydration (manually)
if (isset($data['id'])) {
$this->setId($data['id']);
}
if (isset($data['title'])) {
$this->setTitle($data['title']);
}
if (isset($data['price'])) {
$this->setPrice($data['price']);
}
if (isset($data['book_category_id'])) {
$this->setBookCategoryId($data['book_category_id']);
}
}
/**
* Get Id.
*
* #return int|null
*/
public function getId(): ?int
{
return $this->id;
}
/**
* Set Id.
*
* #param int|null $id
* #return void
*/
public function setId(?int $id): void
{
$this->id = $id;
}
/**
* Get Title.
*
* #return null|string
*/
public function getTitle(): ?string
{
return $this->title;
}
/**
* Set Title.
*
* #param null|string $title
* #return void
*/
public function setTitle(?string $title): void
{
$this->title = $title;
}
/**
* Get Price.
*
* #return float|null
*/
public function getPrice(): ?float
{
return $this->price;
}
/**
* Set Price.
*
* #param float|null $price
* #return void
*/
public function setPrice(?float $price): void
{
$this->price = $price;
}
/**
* Get BookCategoryId.
*
* #return int|null
*/
public function getBookCategoryId(): ?int
{
return $this->bookCategoryId;
}
/**
* Set BookCategoryId.
*
* #param int|null $bookCategoryId
* #return void
*/
public function setBookCategoryId(?int $bookCategoryId): void
{
$this->bookCategoryId = $bookCategoryId;
}
}
The file: BookCategoryEntity.php
class BookCategoryEntity
{
const FANTASY = 1;
const ADVENTURE = 2;
const COMEDY = 3;
// here you can add the setter and getter methods
}
The table structure: schema.sql
CREATE TABLE `books` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`price` decimal(19,2) DEFAULT NULL,
`book_category_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `book_category_id` (`book_category_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE `book_categories` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
/*Data for the table `book_categories` */
insert into `book_categories`(`id`,`title`) values (1,'Fantasy');
insert into `book_categories`(`id`,`title`) values (2,'Adventure');
insert into `book_categories`(`id`,`title`) values (3,'Comedy');
Usage
// Create the database connection
$host = '127.0.0.1';
$dbname = 'test';
$username = 'root';
$password = '';
$charset = 'utf8';
$collate = 'utf8_unicode_ci';
$dsn = "mysql:host=$host;dbname=$dbname;charset=$charset";
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_PERSISTENT => false,
PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES $charset COLLATE $collate"
];
$db = new PDO($dsn, $username, $password, $options);
// Create the data mapper instance
$bookMapper = new BookMapper($db);
// Create a new book entity
$book = new BookEntity();
$book->setTitle('Harry Potter');
$book->setPrice(29.99);
$book->setBookCategoryId(BookCategoryEntity::FANTASY);
// Insert the book entity
$bookId = $bookMapper->insert($book);
// Get the saved book
$newBook = $bookMapper->getById($bookId);
var_dump($newBook);
// Find all fantasy books
$fantasyBooks = $bookMapper->findByBookCategoryId(BookCategoryEntity::FANTASY);
var_dump($fantasyBooks);
I have the following code within a PDO database class
/**
* Class DB
*/
class DB{
/**
* #var DB The one and only instance of this class;
*/
private static $theOneAndOnly = null;
/**
* #var array
*/
private static $transactionalOptions = array( PDO::ATTR_AUTOCOMMIT => false, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC );
/**
* #var array
*/
private static $nonTransactionalOptions = array( PDO::ATTR_AUTOCOMMIT => true, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC );
/**
* #var PDO
*/
private $transactionalDatabase = null;
/**
* #var PDO
*/
private $nonTransactionalDatabase = null;
/**
* #var bool
*/
private $isConnected = false;
/**
* Initializes the connections to the database. One connection for transaction based operations and one connection
* for non-transactional operations;
*/
private function __construct(){
if($this->isConnected() !== true){
//connect
if($this->connect() !== true){
//connection failed
exit( 'An internal error occurred. Please contact Keyfort support:' );
}
}
}
/**
* #return bool
*/
public function isConnected(){
return $this->isConnected;
}
/**
* #return bool
*/
public function connect(){
try{
$this->transactionalDatabase = new PDO(Config::DB_TYPE . ':host=' . Config::DB_HOST . ';dbname=' . Config::$DB_NAME, Config::$DB_USER, Config::$DB_PASS, self::$transactionalOptions);
$this->nonTransactionalDatabase = new PDO(Config::DB_TYPE . ':host=' . Config::DB_HOST . ';dbname=' . Config::$DB_NAME, Config::$DB_USER, Config::$DB_PASS, self::$nonTransactionalOptions);
$this->isConnected = true;
return true;
} catch(PDOException $exception){
Log::error('Initializing the database connections failed:' . $exception->getTraceAsString());
die();
//return false;
}
}
/**
* #return DB
*/
public static function &getInstance(){
if(self::$theOneAndOnly === null){
self::$theOneAndOnly = new self();
}
return self::$theOneAndOnly;
}
public static function ParamMultiple($key){
return self::Param($key) . ', ';
}
public static function Param($key){
return ':' . $key;
}
/**
* Close all connections;
*/
public function __destruct(){
$this->disconnect();
}
/**
* #return void
*/
public function disconnect(){
$this->transactionalDatabase = null;
$this->nonTransactionalDatabase = null;
$this->isConnected = false;
}
/**
* #return PDO
*/
public function &getTransactional(){
if($this->isConnected() !== true){
$this->connect();
}
return $this->transactionalDatabase;
}
/**
* #return PDO
*/
public function &getBasic(){
if($this->isConnected() !== true){
$this->connect();
}
return $this->nonTransactionalDatabase;
}
}
It gets called via DB::getInstance();
The first time it gets called, it does not "die" if it errors.
I eventually get
Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 32 bytes) in C:\path\to\class\db.php on line 64
Line 64 is the $this->transactionalDatabase line.
It does log the exception in my error log (as in the catch).
I'm drawing a blank as to why its not "die"ing, I've tried alot of things, and I'm wondering if it could be starting 2 PDOs in the try?
[edit]
I'm running through the code with the wrong database user password currently. So I know what the error is caused by (it works ok when i put the correct password in), I just don't understand why it isn't processing the die() in the catch.
Use Fragment:
public static function GetByUsername($username){
$db = DB::getInstance();
$dbh = $db->getBasic();
$sql = 'SELECT * FROM ' . self::TABLE_NAME . ' WHERE ' . self::KEY_USERNAME . ' = ' . DB::Param(self::KEY_USERNAME);
$statement = $dbh->prepare($sql);
$statement->bindValue(DB::Param(self::KEY_USERNAME), $username);
if($statement->execute() === true){
$rawUser = $statement->fetch();
if($rawUser === null){
return null;
} else {
return self::CreateFromSelectResult($rawUser);
}
} else {
Log::error("Fetching user by username failed:" . print_r($dbh->errorInfo(), true));
return null;
}
}
Try using either transactionalDatabase or nonTransactionalDatabase, it should solve the problem.
Make sure your /tmp folder has space to cache queries.
The issue was within "Log::error();"
Log:error() also tries to use the database.
So the issue was an infinite loop that started before the die() line was reached.
Is it possible to create the in memory database structure with a DataSet and not using SQL to create the structure?
Currently I have this code, which works. Now I would like to replace the initTable function.
class QueryTest extends \PHPUnit_Extensions_Database_TestCase
{
// only instantiate pdo once for test clean-up/fixture load
static private $pdo = null;
// only instantiate PHPUnit_Extensions_Database_DB_IDatabaseConnection once per test
private $conn = null;
// create connection
public function getConnection()
{
if ($this->conn === null) {
if (self::$pdo == null) {
self::$pdo = new \PDO('sqlite::memory:');
}
$this->conn = $this->createDefaultDBConnection(self::$pdo, "db");
self::initTable();
}
return $this->conn;
}
protected function setUp(){
parent::setUp();
//$this->pdo->query('CREATE TABLE `test`;');
}
public function initTable()
{
$query = "
CREATE TABLE IF NOT EXISTS `guestbook` (
id INT PRIMARY KEY,
content VARCHAR(50) NOT NULL DEFAULT '',
user VARCHAR(20) NOT NULL DEFAULT'',
created VARCHAR(20) NOT NULL DEFAULT ''
)
";
static::$pdo->query($query);
}
public function getDataSet(){
return $this->createXMLDataSet(dirname(__FILE__) . '/../../_files/database.xml');
}
public function testData()
{
$guestbook = new Guestbook();
$guestbook->addEntry("suzy", "Hello world!", self::$pdo);
$queryTable = $this->getConnection()->createQueryTable(
'guestbook', 'SELECT id, content, user FROM guestbook'
);
$expectedTable = $this->createFlatXmlDataSet(dirname(__FILE__) . '/../../_files/expected.xml')
->getTable("guestbook");
$this->assertTablesEqual($expectedTable, $queryTable);
}
}
class Guestbook
{
public function addEntry($name, $message, $pdo)
{
$pdo->query('INSERT INTO `guestbook` (`id`, `content`, `user`) VALUES (3, "' . $message . '", "' . $name . '");');
}
}
I could solve my problem by using this implementation for the unit test.
class QueryPlannerTest extends \PHPUnit_Extensions_Database_TestCase
{
/**
* #var type \PDO
*/
static private $pdo = null;
/**
* #var \PHPUnit_Extensions_Database_DB_IDatabaseConnection
*/
private $conn = null;
/**
* #var QueryPlanner
*/
protected $testObject;
/**
* Connects to in-memory database and retuns a connection.
*
* #return \PHPUnit_Extensions_Database_DB_IDatabaseConnection
*/
public function getConnection()
{
if ($this->conn === null) {
if (self::$pdo == null) {
self::$pdo = new \PDO('sqlite::memory:');
}
$this->conn = $this->createDefaultDBConnection(self::$pdo, "db");
// register the pdo object in the singleton class
$db = Database::getInstance(self::$pdo);
self::initDatabase();
}
return $this->conn;
}
/**
* Returns a dataset for the current tests.
*
* #return \XmlDataSet
*/
public function getDataSet()
{
return $this->createXMLDataSet(dirname(__FILE__) . '/../../datasource/QueryPlanerTestDatabase.xml');
}
/**
* Initializes the in-memory database.
*/
public static function initDatabase()
{
$query = "CREATE TABLE IF NOT EXISTS `user` (
`iduser` INT UNSIGNED,
`email` VARCHAR(100),
`password` VARCHAR(60),
`salt` VARCHAR(22),
`firstname` VARCHAR(150),
`lastname` VARCHAR(150),
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`activated` TINYINT(1) NOT NULL DEFAULT '0',
`activation_code` VARCHAR(10),
`banned` TINYINT(1) NOT NULL DEFAULT '0')";
self::$pdo->query($query);
}
/**
* Resets the database after each test case.
*
* #return \PHPUnit_Extensions_Database_Operation_Truncate
*/
protected function getTearDownOperation()
{
return \PHPUnit_Extensions_Database_Operation_Factory::TRUNCATE();
}
If you use this to set up your unit test you can simply assert the result of a query with your dataset.