September 18, 2015
In this article I will show a simple way to log every change to doctrine 2 entity.
This one will not be copy / paste. Just check out the code and help yourself.
1. Create event service
/**
* @developer Ivan Gospodinow
*/
namespace Application\Service;
use Application\Entity\DbLogEntity;
use Application\Entity\AbstractEntity;
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
use \DateTime;
class DoctrineLogChangesService
{
/**
* Hold the changes
* @var type
*/
protected static $rows = [];
protected $sm;
protected $disabled = [
//'Application\Entity\UserEntity',
];
public function __construct($sm)
{
$this->sm = $sm;
$sm->get(‘Application’)
->getEventManager()
->attach(‘finish ’, [$this, 'flush']);
}
public function flush()
{
$orm = $this->sm->get(‘doctrine.entitymanager.orm_default’);
foreach (self::$rows as $row) {
$orm->persist($row);
}
if (!empty(self::$rows)) {
$orm->flush();
}
}
public function getUnitOfWork()
{
return $this->sm->get(‘doctrine.entitymanager.orm_default’)->getUnitOfWork();
}
public function getEntityTableName(LifecycleEventArgs $args)
{
$meta = $args->getObjectManager()->getClassMetadata(get_class($args->getObject()));
return $meta->table['name'];
}
public function getEntityFields(LifecycleEventArgs $args)
{
$meta = $args->getObjectManager()->getClassMetadata(get_class($args->getObject()));
$fields = [];
foreach ($meta->fieldMappings as $field) {
$fields[$field['fieldName']] = $field['columnName'];
}
foreach ($meta->associationMappings as $field) {
if (isset($field['sourceToTargetKeyColumns'])) {
$fields[$field['fieldName']] = key($field['sourceToTargetKeyColumns']);
}
}
return $fields;
}
/**
* Adds all initial fields
* @param LifecycleEventArgs $args
* @return type
*/
public function postPersist(LifecycleEventArgs $args)
{
$entity = $args->getObject();
if (in_array(get_class($entity), $this->disabled)) {
return;
}
$tableName = $this->getEntityTableName($args);
$fields = $this->getEntityFields($args);
foreach ($fields as $field => $column) {
$value = call_user_func_array([$entity, 'get' . ucfirst($field)], []);
$row = [
'user' => $this->sm->get('User')->getId(),
'table' => $tableName,
'pk' => $entity->getId(),
'column' => $column,
'oldValue' => null,
'newValue' => $value instanceof AbstractEntity ? $value->getId() : $value
];
$log = new DbLogEntity();
$log->exchangeArray($row);
self::$rows[] = $log;
}
}
/**
* Add only new changes
* @param LifecycleEventArgs $args
* @return type
*/
public function preUpdate(LifecycleEventArgs $args)
{
$entity = $args->getObject();
if (in_array(get_class($entity), $this->disabled) || !$entity->canUseLifecycleEvents()) {
return;
}
$tableName = $this->getEntityTableName($args);
$fields = $this->getEntityFields($args);
$set = $this->getUnitOfWork()->getEntityChangeSet($entity);
foreach ($set as $property => $changes) {
$oldValue = $changes[0];
$newValue = $changes[1];
if ($changes[0] instanceof AbstractEntity || $changes[1] instanceof AbstractEntity) {
if ($changes[0] instanceof AbstractEntity) {
$oldValue = $changes[0]->getId();
}
if ($changes[1] instanceof AbstractEntity) {
$newValue = $changes[1]->getId();
}
}
if ($oldValue == $newValue) {
continue;
}
$row = [
‘user’ => $this->sm->get(‘User’)->getId(),
‘ table’ => $tableName,
‘ pk’ => $entity->getId(),
‘ column’ => $fields[$property],
‘ oldValue’ => $this->getStringValue($oldValue),
‘ newValue’ => $this->getStringValue($newValue)
];
$log = new DbLogEntity();
$log->exchangeArray($row);
self::$rows[] = $log;
}
}
public function getStringValue($value)
{
if (is_scalar($value)) {
return $value;
}
if ($value instanceof DateTime) {
return $value->format('Y-m-d H:i:s');
} elseif (is_array($value)) {
return serialize($value);
}
return $value;
}
}
2. Add the event service to Doctrine 2
/**
* @developer Ivan Gospodinow
*/
namespace Application;
use Zend\Mvc\MvcEvent;
class Module
{
public function onBootstrap(MvcEvent $e)
{
$application = $e->getApplication();
$sm = $application->getServiceManager();
$doctrineEntityManager = $sm->get('doctrine.entitymanager.orm_default');
$doctrineEventManager = $doctrineEntityManager->getEventManager();
$doctrineEventManager->addEventListener(
[
\Doctrine\ORM\Events::postPersist,
\Doctrine\ORM\Events::preUpdate,
],
new Service\DoctrineLogChangesService($sm)
);
}
}
3. DbLogEntity
/**
* @developer Ivan Gospodinow
*/
namespace Application\Entity;
use Doctrine\ORM\Mapping as ORM;
use \Exception;
/**
* @ORM\Table(name="__dblog")
* @ORM\Entity()
*/
class DbLogEntity
{
/**
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
* @ORM\Column(type="integer", name="id")
*/
protected $id;
/**
* @ORM\Column(type="string", name="user_id")
*/
protected $user;
/**
* @ORM\Column(type="string", name="table_name")
*/
protected $table;
/**
* @ORM\Column(type="integer", name="pk")
*/
protected $pk;
/**
* @ORM\Column(type="string", name="column_name")
*/
protected $column;
/**
* @ORM\Column(type="string", name="old_value")
*/
protected $oldValue;
/**
* @ORM\Column(type="string", name="new_value")
*/
protected $newValue;
/**
* @ORM\Column(type="datetime", name="timestamp")
*/
protected $timestamp;
}
Simple as that.
Suggestions or problems ? Write a comment.
tags: Doctrine 2, how to, php, zf2
posted in Doctrine 2, how to ?, Zend Framework 2 by Ivan Gospodinow | No Comments
September 15, 2015
How to change layout for ajax requests in Zend Framework 2
1. Module.php
public function onBootstrap(MvcEvent $e)
{
$e->getApplication()
->getEventManager()
->getSharedManager()
->attach(
'Zend\Mvc\Controller\AbstractController',
'dispatch',
function ($e) {
$controller = $e->getTarget();
if ($controller->getRequest() instanceof HttpRequest) {
if ($controller->getRequest()->isXmlHttpRequest()) {
$controller->layout('layout/ajax');
}
}
},
100
);
}
2. module.config.php Add the layout path
'view_manager' => array(
'template_map' => array(
'layout/ajax' => __DIR__ . '/../view/layout/layout.ajax.phtml',
),
),
tags: php, zf2
posted in Zend Framework 2 by Ivan Gospodinow | No Comments
September 12, 2015
1. Creating Jira issue
$jiraUsername = 'Jira login username';
$jiraPassword = 'Jira login password';
$jiraHost = 'http://my-jira-host.com/';
$data = array(
'fields' => array(
'project' => array('key' => 'PR'/* Project Key*/),
'summary'=> 'Issue title',
'description' => 'Some text here, no html tags',
'labels' => array('API', 'IvanGospodinow'/* Put the username so you know which one added it */),
'issuetype' => array('id' => 1 /* or similar */)
)
);
$ch = curl_init();
$headers = array(
'Accept: application/json',
'Content-Type: application/json'
);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_VERBOSE, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_URL, $jiraHost . 'rest/api/2/issue/');
curl_setopt($ch, CURLOPT_USERPWD, "$jiraUsername:$jiraPassword");
$response = curl_exec($ch);
$error = curl_error($ch);
if (!empty($error)) {
throw new Exception('EQ Error: ' . $error);
}
curl_close($ch)
2. Getting all issues for user
$ch = curl_init();
$headers = array(
'Accept: application/json',
'Content-Type: application/json'
);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_VERBOSE, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_URL, $jiraHost . 'rest/api/2/search?jql=' . urlencode('labels in (API, 'IvanGospodinow')'));
curl_setopt($ch, CURLOPT_USERPWD, "$jiraUsername:$jiraPassword");
$response = curl_exec($ch);
curl_close($ch);
Simple as that.
tags: curl, jira, php
posted in php by Ivan Gospodinow | No Comments
September 7, 2015
Module.php
public function getConfig()
{
return
(include __DIR__ . '/config/module.config.php')
+
(include __DIR__ . '/config/module.config.permissions.php');
}
Simple as that.
posted in Zend Framework 2 by Ivan Gospodinow | No Comments
July 17, 2015
1. Dump structure of the big database.
2. Open the sql file and cut the constraints.
3. Import the structure to another database.
4. Run this code:
/**
* Sync from live database
*/
ini_set('memory_limit','512M');
$limit = 100000;
$time = time();
$sleep = 1;
$sleepEvery = 60;
$link = new mysqli(
"__HOST__",
"__USER__",
"__PASSWORD__",
"__DATABASE__"
);
$link->set_charset("utf8");
$importLink = new mysqli(
"__HOST__",
"__USER__",
"__PASSWORD__",
"__DATABASE__"
);
$importLink->set_charset("utf8");
$result = $link->query('SHOW TABLES');
$tables = [];
while($row = mysqli_fetch_assoc($result)) {
/**
* Filter the tables if you want
*/
$table = $row[key($row)];
$tables[] = $table;
}
foreach ($tables as $table)
{
$offset = 0;
$hasRows = true;
while ($hasRows) {
echo "Excecuting SELECT * FROM `$table` LIMIT $limit OFFSET $offset" . PHP_EOL;
flush();
$result = $link->query("SELECT * FROM `$table` LIMIT $limit OFFSET $offset");
if (!$result) {
$hasRows = false;
break;
}
$rows = [];
while($row = mysqli_fetch_assoc($result)) {
$rows[] = $row;
}
if (!empty($rows)) {
dumpToDatabase($table, $rows);
}
$offset += $limit;
takeANap();
if (count($rows) < $limit) {
$hasRows = false;
break;
}
}
}
function takeANap()
{
global $time, $sleep, $sleepEvery;
if ($time + $sleepEvery <= time()) {
$time = time();
echo 'Sleep for ' . $sleep . PHP_EOL;
flush();
sleep($sleep);
}
}
function dumpToDatabase($table, $rows)
{
global $importLink;
echo 'begin_transaction' . PHP_EOL;
$importLink->begin_transaction();
$inserts = [];
foreach ($rows as $row) {
$values = [];
foreach ($row as $id => $value) {
$values[] = "'".mysqli_real_escape_string($importLink, $value)."'";
}
$inserts[] = sprintf(
'(%s)',
implode(',', $values)
);
if (count($inserts) >= 1000) {
$str = sprintf(
'INSERT INTO `%s` (%s) VALUES %s;',
$table,
implode(',', array_keys($row)),
implode(',', $inserts)
);
$importLink->query($str);
echo mysqli_error($importLink) . PHP_EOL;
$inserts = [];
}
}
if (!empty($inserts)) {
$str = sprintf(
'INSERT INTO `%s` (%s) VALUES %s;',
$table,
implode(',', array_keys($row)),
implode(',', $inserts)
);
$importLink->query($str);
}
echo 'commit' . PHP_EOL;
$importLink->commit();
}
die('END');
5. Add the constrains from step 2.
Easy as that.
posted in how to ?, php by Ivan Gospodinow | No Comments
September 26, 2014
$sum = [0, 0, 0, 0];
echo count($sum) / array_sum($sum);
// Warning: Division by zero
$arraySum = 0.00001 + array_sum($sum);
$number = count($sum) / $arraySum;
echo round($number, 2);
// Result is 0;
Just add some small number to the one you are not sure that it will be bigger than zero.
Easy as that.
posted in php by Ivan Gospodinow | No Comments
July 5, 2014
Long time no ZF2 post, but here is something interesting peace of code that can solve cache problems present in most of the projects.
I am assuming that, the reader knows how to create custom cache Factory in ZF2.
Step 1.
Edit your existing cache factory.
See Line 35.
namespace Application\Factory;
use Application\Factory\CacheProxy; // Or whatever location
use Zend\Cache\Storage\Plugin\Serializer;
use Zend\Cache\StorageFactory;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class CacheFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $sm)
{
$config = $sm->get('config');
$cache = StorageFactory::factory(array(
'adapter' => array(
'name' => 'filesystem',
),
'plugins' => array(
// Don't throw exceptions on cache errors
'exception_handler' => array(
'throw_exceptions' => true
),
)
));
if (!file_exists($config['cache']['cache_dir'])) {
mkdir($config['cache']['cache_dir'], 0755, true);
}
$cache->getOptions()->setTtl((int) $config['cache']['cache_ttl']);
$cache->getOptions()->setCacheDir($config['cache']['cache_dir']);
$cache->addPlugin(new Serializer);
return new CacheProxy($cache);
}
}
Step 2.
CacheProxy Code
namespace Application\Factory;
use Zend\Cache\Storage\StorageInterface;
class CacheProxy
{
/**
*
* @var \Zend\Cache\Storage\StorageInterface
*/
protected $cache;
/**
* We are using the fact that $key is the first parameter
* and pass by reference is not present.
* @var type
*/
protected $override = [
'hasItem',
'getMetadata',
'setItem',
'addItem',
'replaceItem',
'touchItem',
'removeItem',
'incrementItem',
'decrementItem'
];
public function __construct(StorageInterface $cache)
{
$this->cache = $cache;
}
/**
*
* @param type $name
* @param type $arguments
* @return type
*/
public function __call($name, $arguments)
{
if (in_array($name, $this->override)) {
$arguments[0] = $this->getKey($arguments[0]);
}
$return = call_user_func_array([$this->cache, $name], $arguments);
/**
* Ensure that chain calls will work as expected.
*/
if ($return instanceof StorageInterface) {
return new self($return);
} else {
return $return;
}
}
/**
*
* @param type $key
* @param type $success
* @param type $casToken
*/
public function getItem($key, &$success = null, &$casToken = null)
{
return $this->cache->getItem($this->getKey($key), $success, $casToken);
}
/**
* @param type $token
* @param type $key
* @param type $value
* @return type
*/
public function checkAndSetItem($token, $key, $value)
{
return $this->cache->replaceItem($token, $this->getKey($key), $value);
}
/**
*
* @param type $key
* @return type
*/
protected function getKey($key)
{
return sha1(
sprintf(
'%s-%s-%s',
$key,
defined('APPLICATION_ENV') ? APPLICATION_ENV : '',
defined('APPLICATION_VERSION') ? APPLICATION_VERSION : ''
)
);
}
}
Simple as that.
Suggestions or problems ? Write a comment.
tags: OOP, Zend Framework 2
posted in OOP, Zend Framework 2 by Ivan Gospodinow | No Comments
August 21, 2013
As always we go straight to coding.
Download how-to-create-google-login-on-my-site-with-openid-in-5-min.zip
mode) {
// the google openid url
$openid->identity = 'https://www.google.com/accounts/o8/id';
// information for for the user
$openid->required = array('contact/email', 'namePerson/first', 'namePerson/last');
// redirect to google
header('Location: ' . $openid -> authUrl());
} elseif ($openid->mode == 'cancel') {
// user cancel the login
// redirect may be ?
} else {
if ($openid->validate()) {
// user logged in
$d = $openid->getAttributes();
$first_name = $d['namePerson/first'];
$last_name = $d['namePerson/last'];
$email = $d['contact/email'];
// do your login
} else {
//user is not logged in
}
}
} catch(Exception $e) {
// something went wrong
echo $e->getMessage();
exit;
}
Simple as that.
Suggestions or problems ? Write a comment.
tags: google, how to, php
posted in how to ?, php by Ivan Gospodinow | No Comments
June 17, 2013
Right to the example…
$identifier = spl_object_hash(function(){
// some function content
});
Simple as that.
Suggestions or problems ? Write a comment.
posted in how to ?, OOP, php by Ivan Gospodinow | No Comments
June 16, 2013
function csvToArray($file){
$rows = array();
$headers = array();
if(file_exists($file) && is_readable($file)){
$handle = fopen($file, 'r');
while (!feof($handle) ) {
$row = fgetcsv($handle, 10240);
if(empty($headers))
$headers = $row;
else if(is_array($row))
$rows[] = array_combine($headers, $row);
}
fclose($handle);
} else {
throw new Exception($file.' doesn`t exist or is not readable.');
}
return $rows;
}
Simple as that.
Suggestions or problems ? Write a comment.
tags: csv, how to, php
posted in how to ?, php by Ivan Gospodinow | No Comments