Symfony
[rb]Custom doctrine count query
0Sometimes you may need to use count with the index column rather than *, because of some dark corners of sql driven databases. Though this is somehow rare, this is how you could do it with Doctrine … this example is part of a symfony 1.4 project(place the file in any of the /lib dirs … app ones or global one):
<?php
class Custom_Doctrine_Query extends Doctrine_Query {
public static function create($conn = null, $class = null)
{
if ( ! $class) {
$class = 'Custom_Doctrine_Query';
}
return new $class($conn);
}
public function getCustomCountSqlQuery()
{
// triggers dql parsing/processing
$this->getSqlQuery(array(), false); // this is ugly
// initialize temporary variables
$where = $this->_sqlParts['where'];
$having = $this->_sqlParts['having'];
$groupby = $this->_sqlParts['groupby'];
$rootAlias = $this->getRootAlias();
$tableAlias = $this->getSqlTableAlias($rootAlias);
$primaryKey = $tableAlias . '.' . $this->_queryComponents[$rootAlias]['table']->getColumnName( $this->_queryComponents[$rootAlias]['table']->getIdentifier());
// Build the query base
$q = 'SELECT COUNT(' . $primaryKey . ') AS ' . $this->_conn->quoteIdentifier('num_results') . ' FROM ';
// Build the from clause
$from = $this->_buildSqlFromPart(true);
// Build the where clause
$where = ( ! empty($where)) ? ' WHERE ' . implode(' ', $where) : '';
// Build the group by clause
$groupby = ( ! empty($groupby)) ? ' GROUP BY ' . implode(', ', $groupby) : '';
// Build the having clause
$having = ( ! empty($having)) ? ' HAVING ' . implode(' AND ', $having) : '';
// Building the from clause and finishing query
if (count($this->_queryComponents) == 1 && empty($having)) {
$q .= $from . $where . $groupby . $having;
} else {
// Subselect fields will contain only the pk of root entity
$ta = $this->_conn->quoteIdentifier($tableAlias);
$map = $this->getRootDeclaration();
$idColumnNames = $map['table']->getIdentifierColumnNames();
$pkFields = $ta . '.' . implode(', ' . $ta . '.', $this->_conn->quoteMultipleIdentifier($idColumnNames));
// We need to do some magic in select fields if the query contain anything in having clause
$selectFields = $pkFields;
if ( ! empty($having)) {
// For each field defined in select clause
foreach ($this->_sqlParts['select'] as $field) {
// We only include aggregate expressions to count query
// This is needed because HAVING clause will use field aliases
if (strpos($field, '(') !== false) {
$selectFields .= ', ' . $field;
}
}
// Add having fields that got stripped out of select
preg_match_all('/`[a-z0-9_]+`\.`[a-z0-9_]+`/i', $having, $matches, PREG_PATTERN_ORDER);
if (count($matches[0]) > 0) {
$selectFields .= ', ' . implode(', ', array_unique($matches[0]));
}
}
// If we do not have a custom group by, apply the default one
if (empty($groupby)) {
$groupby = ' GROUP BY ' . $pkFields;
}
$q .= '(SELECT ' . $selectFields . ' FROM ' . $from . $where . $groupby . $having . ') '
. $this->_conn->quoteIdentifier('dctrn_count_query');
}
return $q;
}
public function count($params = array())
{
$q = $this->getCustomCountSqlQuery();
$params = $this->getCountQueryParams($params);
$params = $this->_conn->convertBooleans($params);
if ($this->_resultCache) {
$conn = $this->getConnection();
$cacheDriver = $this->getResultCacheDriver();
$hash = $this->getResultCacheHash($params).'_count';
$cached = ($this->_expireResultCache) ? false : $cacheDriver->fetch($hash);
if ($cached === false) {
// cache miss
$results = $this->getConnection()->fetchAll($q, $params);
$cacheDriver->save($hash, serialize($results), $this->getResultCacheLifeSpan());
} else {
$results = unserialize($cached);
}
} else {
$results = $this->getConnection()->fetchAll($q, $params);
}
if (count($results) > 1) {
$count = count($results);
} else {
if (isset($results[0])) {
$results[0] = array_change_key_case($results[0], CASE_LOWER);
$count = $results[0]['num_results'];
} else {
$count = 0;
}
}
return (int) $count;
}
}
The usage of the above class should be something like this:
$query = Custom_Doctrine_Query::create()->from('TargetTable t');
$count = $query->count();
or the bulky way omitting the static create method:
$query = Doctrine_Query::create(null, 'Custom_Doctrine_Query')->from('TargetTable t');
$count = $query->count();
The result of the above queries should be something like “SELECT COUNT(index_field) from target_table”.
WTH swiftmailer
0Now it’s kindda obvious that swift mailer is the best php mail library, but as any other great php library it sometimes lets you right in the middle of nowhere, when you’re thinking everything should be fine. This was the case with a freshly downloaded version … everything went fine until Swift_KeyCache_KeyCacheInputStream interface not found in … to shorten the story … swift looks for that interface which is expected to be in the file KeyCache.php … the declaration seems to be missing from the downloaded package … the fix is pretty simple(though there is probably a good reason why it isn’t there anymore, but for a quick fix will do) … open /classes/Swift/KeyCache.php and add these lines:
interface Swift_KeyCache_KeyCacheInputStream extends Swift_InputByteStream
{
public function setKeyCache(Swift_KeyCache $keyCache);
public function setNsKey($nsKey);
public function setItemKey($itemKey);
public function setWriteThroughStream(Swift_InputByteStream $is);
public function __clone();
}
save it … and you’re good to go.
[rb]symfony’s 1.4 sfForm isValid()
0Someone asked me a few days ago how can he use the sfform validation feature to validate an object created “by hand”. While i’m not quite sure yet of the usage of such a thing, here’s my approach(we’ll assume we have a table called Selections with 4 fields like id(incremental/key/bigint), target_id(bigint), type(varchar), name(varchar)):
public function executeAdd(sfWebRequest $request) {
if($request->isXmlHttpRequest()) {
$this->setLayout(false);
$this->forward404Unless($id = $request->getParameter('id'));
$this->forward404Unless($type = $request->getParameter('type'));
$selection = new Selections();
$selection->setTargetId($id);
$selection->setType(ucfirst($type));
$selection->setName('DEFAULT');
$form = new SelectionsForm(null, array(), false);
$values = $selection->toArray();
$form->bind($values);
if($form->isValid()) { $form->save(); echo 'OK'; }
else {
echo 'ERROR';
}
return sfView::NONE;
} else $this->forward404();
}
[rb]Debug a symfony object
2While the symfony framework is quite intuitive and mostly easy to understand you might sometimes need to debug an object to see it’s methods or simply have a general overview(if you’r lazy enough not to check the official documentation). This might come handy sometimes:
<?php
class Debugger {
public static function debug($class, $exiting = true) {
if(!is_object($class)) return;
$methods = get_class_methods($class);
natsort($methods);
echo '<h1>' . get_class($class) . '</h1>';
echo '<hr size="1" />';
array_walk($methods, array(self, "format"));
if($exiting) exit();
}
public static function format(&$item1, $key) {
if(strpos($item1,"__") > -1) $item1 = '<span style="background: #ff0000">' . $item1 . '</span>';
elseif(strpos($item1, "get") > -1) $item1 = '<span style="background: #cceeff">' . $item1 . '</span>';
elseif(strpos($item1, "set") > -1) $item1 = '<span style="background: #eeffcc">' . $item1 . '</span>';
else $item1 = '<span style="background: #ddd">' . $item1 . '</span>';
echo $item1 . "<br />";
}
}
?>
Save it in the lib dir of your application under the name Debugger.php then call it like this
Debugger::debug($symfony_object_you_want_to_see);
[rb]Get relative url with symfony
1While using symfony in a subdirectory of your domain you might want to get the absolute URL path of you web directory. This should help you:
function getUrlLocation() {
$s = $_SERVER['DOCUMENT_ROOT'];
$t = explode(DIRECTORY_SEPARATOR, sfConfig::get('sf_web_dir'));
$tx = explode(DIRECTORY_SEPARATOR,$s);
$dif = array_diff($t,$tx);
return implode(DIRECTORY_SEPARATOR, $dif);
}
A practical example would be redirecting from a public module to the backend module for example. That would be accomplished by using the following code:
header("Location: /". getUrlLocation() . "/backend.php"); exit();
NetBeans launches support for symfony
0Probably there are a few people that have been expecting this for some time. Netbeans support for symfony is finally here.
http://blogs.sun.com/netbeansphp/entry/symfony_support_finished
[rb] sfWebBrowser login asp.net
1* this post depends on the work of Fabien Potencier and he’s symfony library together with the work of Francois Zaninotto and he’s sfWebBrowser Plugin for the symfony framework
** if using mozilla browser use firebug and it’s Net function with the “Persist” button ON.
function executeSomeaction(sfWebRequest $r) {
//set some headers, so we'll be ridding mozilla web browser
$b = new sfWebBrowser(array("Accept" => "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language" => "en-gb,en;q=0.8,en-us;q=0.6,fr-fr;q=0.4,en;q=0.2", "Accept-Encoding"=> "gzip,deflate", "Accept-Charset" => "UTF-8,*", "Keep-Alive" => "115", "Connection" => "keep-alive"), 'sfCurlAdapter', array('cookies' => true, 'followlocation' => false, "verbose_log" => true));
//not sure if necessary, but get the "new" asp.net hidden fields
$content = $b->get('http://someweb/someloginform')->getResponseBody();
$view_state = preg_match("/<[^>]*name=\"__VIEWSTATE\"[^>]*value=\"([^\"]+)[^>]*>/i", $content, $vc);
$event_validation = preg_match("/<[^>]*name=\"__EVENTVALIDATION\"[^>]*value=\"([^\"]+)[^>]*>/i", $content, $vd);
//print_r($vd);exit();
$b->post('http://someweb/someformpostpage', array(
'ImageButtonLogin.x' => 26,
'ImageButtonLogin.y' => 15,
'TextBoxPassword' => 'somepassword',
'TextBoxUserName' => 'someusername',
'__EVENTVALIDATION' => $vd[1],
'__VIEWSTATE' => $vc[1]
))
}
