![]() |
|
Snippets |
|
<textarea id="comment" style="height:35px;max-height:100px; line-height:12px; font-size:12px;" class="span-24 last"></textarea>
<div id="b" class="span-24 last"> <button id="vid">Vid</button> <button id="pic">Pic</button> </div> [javascript] <script type="text/javascript"> $(document).ready(function() { var vidIndex = 0; var vidList = new Array(); $('#vid').click(function() { $('#vidContainer').slideDown(); }); $('#closeVid').click(function() { $('#vidContainer').slideUp(); }); $('#addVid').click(function() { $('#vidList').append('<div id="vid'+vidIndex+'"><span class="video">' + $('#vidUrl').val() + '</span> <button title="'+vidIndex+'" class="removeVid">Remove</button></div>'); ++vidIndex; var chosen = ''; $('.video').each(function () { chosen += $(this).text() + ','; }); $('#params').html(chosen); }); $('.removeVid').live("click", function(){ var index = $(this).attr('title'); $('#vid'+index).remove(); --vidIndex; var chosen = ''; $('.video').each(function () { chosen += $(this).text() + ','; }); $('#params').html(chosen); }); </script>
In case you have a populated database and you need to generate a fixtures for it, you can use symfony propel:data-dump tasks or manually call methods of sfPropelData.
But If your database contains too much data to be fetched, this methods will not work for you.
For such cases I have extended sfPropelData to use Propel Criteria objects to filter dumped data. For example, you can set limit 30 records for every model to prevent dumping all records from database.
You can create special tasks to generate fixtures and use there class listed below
$data = new fxPropelData(); // Defining criterias $c1 = new Criteria(); $c1->setLimit(30); $c2 = clone($c1); $c2->add('IS_DELETED',0); // array with models and criterias for them $models = array( 'Fruit'=> $c1, 'Vegetable' => $c2, ); $data->dumpDataWithCriteria(sfConfig::get('sf_data_dir').'/fixtures/food.yml',$models, $options['connection']);
class fxPropelData extends sfPropelData { public function dumpDataWithCriteria($directoryOrFile, $models = array(), $connectionName = null) { $dumpData = $this->getDataWithCriteria($models, $connectionName); // save to file(s) if (!is_dir($directoryOrFile)) { file_put_contents($directoryOrFile, sfYaml::dump($dumpData, 3)); } else { $i = 0; foreach ($models as $tableName => $criteria) { if (!isset($dumpData[$tableName])) { continue; } file_put_contents(sprintf("%s/%03d-%s.yml", $directoryOrFile, ++$i, $tableName), sfYaml::dump(array($tableName => $dumpData[$tableName]), 3)); } } } protected function getDataWithCriteria($models, $connectionName = 'propel') { $this->loadMapBuilders(); $this->con = Propel::getConnection($connectionName); $this->dbMap = Propel::getDatabaseMap($connectionName); $dumpData = array(); foreach ($models as $model => $criteria) { $tableName = $model; $tableMap = $this->dbMap->getTable(constant(constant($tableName.'::PEER').'::TABLE_NAME')); $hasParent = false; $haveParents = false; $fixColumn = null; foreach ($tableMap->getColumns() as $column) { $col = strtolower($column->getName()); if ($column->isForeignKey()) { $relatedTable = $this->dbMap->getTable($column->getRelatedTableName()); if ($tableName === $relatedTable->getPhpName()) { if ($hasParent) { $haveParents = true; } else { $fixColumn = $column; $hasParent = true; } } } } if ($haveParents) { // unable to dump tables having multi-recursive references continue; } // get db info $resultsSets = array(); if ($hasParent) { $resultsSets[] = $this->fixOrderingOfForeignKeyDataInSameTableWithCriteria($resultsSets, $criteria, $tableName, $fixColumn); } else { $stmt = call_user_func(array($tableName.'Peer', 'doSelectStmt'),$criteria,$this->con); $resultsSets[] = array_merge($stmt->fetchAll(PDO::FETCH_ASSOC),$resultsSets); $stmt->closeCursor(); unset($stmt); } foreach ($resultsSets as $rows) { if(count($rows) > 0) { if (!isset($dumpData[$tableName])) $dumpData[$tableName] = array(); foreach ($rows as $row) { $pk = $tableName; $values = array(); $primaryKeys = array(); $foreignKeys = array(); foreach ($tableMap->getColumns() as $column) { $col = $column->getName(); $isPrimaryKey = $column->isPrimaryKey(); if (is_null($row[$col])) { continue; } if ($isPrimaryKey) { $value = $row[$col]; $pk .= '_'.$value; $primaryKeys[$col] = $value; } if ($column->isForeignKey()) { $relatedTable = $this->dbMap->getTable($column->getRelatedTableName()); if ($isPrimaryKey) { $foreignKeys[$col] = $row[$col]; $primaryKeys[$col] = $relatedTable->getPhpName().'_'.$row[$col]; } else { $values[$col] = $relatedTable->getPhpName().'_'.$row[$col]; $values[$col] = strlen($row[$col]) ? $relatedTable->getPhpName().'_'.$row[$col] : ''; } } elseif (!$isPrimaryKey || ($isPrimaryKey && !$tableMap->isUseIdGenerator())) { // We did not want auto incremented primary keys $values[$col] = $row[$col]; } } if (count($primaryKeys) > 1 || (count($primaryKeys) > 0 && count($foreignKeys) > 0)) { $values = array_merge($primaryKeys, $values); } $dumpData[$tableName][$pk] = $values; } } } } return $dumpData; } protected function fixOrderingOfForeignKeyDataInSameTableWithCriteria($resultsSets, $criteria, $tableName, $column, $in = null) { is_null($in) ? $criteria->add($column->getName(),NULL) : $criteria->add($column->getName(),$in,Criteria::IN); $stmt = call_user_func(array($tableName.'Peer', 'doSelectStmt'),$criteria,$this->con); $in = array(); while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { $in[] = "'".$row[strtolower($column->getRelatedColumnName())]."'"; $resultsSets[] = $row; } if ($in = implode(', ', $in)) { $resultsSets = $this->fixOrderingOfForeignKeyDataInSameTableWithCriteria($resultsSets, $tableName, $column, $in); } return $resultsSets; } }
The symfony routing system is tied to the sfContext resource via the factory handler.
It might an issue if you need to build routes in the cli mode, where sfContext has no instance. Sample: one of your cron jobs sends emails that contain links.
The best way I've found is to provide an access to the routing system linked to the ProjectConfiguration class. Add a static protected "$routing" variable and a static public method "getRouting()" :
class ProjectConfiguration extends sfProjectConfiguration { static protected $routing = null # Symfony routing system ; /** * Returns the routing resource for the active application */ static public function getRouting() { if (null !== self::$routing) { return self::$routing; } // If sfContext has an instance, returns the loaded routing resource if (sfContext::hasInstance() && sfContext::getInstance()->getRouting()) { self::$routing = sfContext::getInstance()->getRouting(); } else { // Initialization if (!self::hasActive()) { throw new sfException('No sfApplicationConfiguration loaded'); } $appConfig = self::getActive(); $config = sfFactoryConfigHandler::getConfiguration($appConfig->getConfigPaths('config/factories.yml')); $params = array_merge( $config['routing']['param'], array( 'load_configuration' => false, 'logging' => false, 'context' => array( 'host' => sfConfig::get('app_host', 'localhost'), 'prefix' => sfConfig::get('app_prefix', sfConfig::get('sf_no_script_name') ? '' : '/'.$appConfig->getApplication().'_'.$appConfig->getEnvironment().'.php'), 'is_secure' => sfConfig::get('app_host', false), ), ) ); $handler = new sfRoutingConfigHandler(); $routes = $handler->evaluate($appConfig->getConfigPaths('config/routing.yml')); $routeClass = $config['routing']['class']; self::$routing = new $routeClass($appConfig->getEventDispatcher(), null, $params); self::$routing->setRoutes($routes); } return self::$routing; } }
In the method, the 'context' parameter needs further explanations.
In a classic context, symfony is loaded after a web request. Sample: https://www.example.com/frontend_dev.php/products
This request provides data that are reused when sf needs to generate a new dynamic url:
In the cli mode, as Symfony is not loaded by a request, you have to provide these parameters (prefix can be guessed).
In app.yml, add:
all:
host: www.example.com
is_secure: false
Now, let's see how to generate the link. If your routing.yml file looks like it:
product_show: url: /products/:category_slug/:slug class: sfDoctrineRoute options: { model: Product, type: object } # ...
Then building the link is as simple as:
class Product extends BaseProduct { /** * Returns the public url */ public function getUrl() { return ProjectConfiguration::getRouting()->generate('product', $this, true); } /* ... */ }
Regards.
For sf1.3/1.4 only.
It seems that the symfony mailer system is tied to the sfContext resource via the factory handler (correct me if I'm wrong).
It might be a problem when you want to send an email from one of your model methods because sfContext has no instance in cli mode.
It can also be an issue if you want to build a task that send emails. Sample: a nightly cron job that alert users by email when their free trial period is over.
The best way I've found is to provide an access to the mailer system linked to the ProjectConfiguration class.
Add a static protected "$mailer" variable and a static public method "getMailer()" :
class ProjectConfiguration extends sfProjectConfiguration { static protected $mailer = null # Symfony mailer system ; /** * Returns the project mailer */ static public function getMailer() { if (null !== self::$mailer) { return self::$mailer; } // If sfContext has instance, returns the classic mailer resource if (sfContext::hasInstance() && sfContext::getInstance()->getMailer()) { self::$mailer = sfContext::getInstance()->getMailer(); } else { // Else, initialization if (!self::hasActive()) { throw new sfException('No sfApplicationConfiguration loaded'); } require_once sfConfig::get('sf_symfony_lib_dir').'/vendor/swiftmailer/classes/Swift.php'; Swift::registerAutoload(); sfMailer::initialize(); $applicationConfiguration = self::getActive(); $config = sfFactoryConfigHandler::getConfiguration($applicationConfiguration->getConfigPaths('config/factories.yml')); self::$mailer = new $config['mailer']['class']($applicationConfiguration->getEventDispatcher(), $config['mailer']['param']); } return self::$mailer; } }
Now if you want to send an email from a task or from a model class:
ProjectConfiguration::getMailer()->composeAndSend(/* your email params*/);
In your unit tests, if you want to test which emails have been sent:
$sentEmails = ProjectConfiguration::getMailer()->getLogger()->getMessages();
Regards.
To fetch a random user:
$userCount = Doctrine::getTable('User')->count(); $user = Doctrine::getTable('User') ->createQuery() ->limit(1) ->offset(rand(0, $userCount - 1)) ->fetchOne();
Stemming reduces a word to its common stem, so if you search on Google for "Knit", you'll also be searching for "Knits" and "Knitting". Sadly, Doctrine's Searchable behaviour doesn't support stemming out of the box, but it's pretty easy to add.
I've chosen to use the PECL 'stem' extension in this implementation because it contains the excellent Porter2 stemmer. You may be able to find a free pure PHP implementation of the algorithm somewhere, or decide that the original Porter stemmer is good enough. I couldn't.
You'll need to compile and install the PECL stem extension, and make sure it's loaded.
Create lib/MyAnalyzer.class.php:
class MyAnalyzer extends Doctrine_Search_Analyzer_Standard { public function analyze($text) { // First run the standard analyzer. This will do a lot of tidying on // the text and remove stopwords, so we only need to do the stemming $text = parent::analyze($text); foreach ($text as &$keyword) { $keyword = stem_english($keyword); } return $text; } }
First, create a doctrine listener, lib/MyConnectionListener.class.php
class MyConnectionListener extends Doctrine_EventListener { public function postConnect(Doctrine_Event $e) { $entity_table = Doctrine_Core::getTable('Entity'); $entity_table->getTemplate('Doctrine_Template_Searchable') ->getPlugin() ->setOption('analyzer', new MyAnalyzer); } } }
Then edit your ProjectConfiguration class:
public function configureDoctrineConnection(Doctrine_Connection $conn) { $conn->addListener(new MyConnectionListener) }
Finally, in your frontend code, make sure you stem your search terms before running a query.
$terms = 'my search string'; $stemmer = new MyAnalyzer(); $terms = join($stemmer->analyze($terms), ' '); $results = Doctrine::getTable('Entity')->search($terms);
A couple of notes:
Doctrine's built-in support for multiple connections follows a baffling use-case that allows different Model objects to be saved on different connections. A more common usage of multiple connections is to send read commands to a slave database and write commands to a master. Here's how to do it.
Declare your connections in databases.yml as usual, name one of them master and one of them slave.
Override Doctrine's default Query and Record objects. Edit config/ProjectConfiguration.class.php and add the following method:
public function configureDoctrine(Doctrine_Manager $manager) { // Configure custom query and custom record classes $manager->setAttribute(Doctrine::ATTR_QUERY_CLASS, 'MyQuery'); $options = array('baseClassName' => 'MyRecord'); sfConfig::set('doctrine_model_builder_options', $options); }
Create your MyQuery class in lib/MyQuery
class MyQuery extends Doctrine_Query { public function preQuery() { // If this is a select query then set connection to the slave if (Doctrine_Query::SELECT == $this->getType()) { $this->_conn = Doctrine_Manager::getInstance()->getConnection('slave'); // All other queries are writes so they need to go to the master } else { $this->_conn = Doctrine_Manager::getInstance()->getConnection('master'); } } }
Create your MyRecord class in lib/MyRecord.class.php
abstract class MyRecord extends sfDoctrineRecord { public function save(Doctrine_Connection $conn = null) { $conn = Doctrine_Manager::getInstance()->getConnection('master'); parent::save($conn); } }
If you're using a Doctrine behaviour that creates new tables, like the Versionable behaviour, tell it to use MyRecord too.
Entity:
ActAs:
Versionable:
builderOptions:
baseClassName: MyRecord
Finally, rebuild your model
./symfony doctrine:build --all-classes
If you have multiple slaves, modify the ProjectConfiguration to pick one on startup then change the method on your Query object to retrieve it.
I happened to come across this problem - parse referer to get its module and action. I thought there was an official way to do this in Symfony. But it turns out not(At least 1.0, maybe I didn't dig deep enough, then please inform me). I don't like to parse it by myself either, so after digging into symfony's source code. I come up with below solution:
function getModuleActionFrom($absurl) { $_uri = $_SERVER['REQUEST_URI']; if (isset($_SERVER['PATH_INFO'])) $_path = $_SERVER['PATH_INFO']; $_SERVER['REQUEST_URI'] = parse_url($absurl, PHP_URL_PATH); if (isset($_SERVER['PATH_INFO'])) $_SERVER['PATH_INFO'] = null; $tempReq = new sfWebRequest(); $tempReq->initialize(sfContext::getInstance()); $result = array( 'module' => $tempReq->getParameter('module'), 'action' => $tempReq->getParameter('action') ); $_SERVER['REQUEST_URI'] = $_uri; if (isset($_SERVER['PATH_INFO'])) $_SERVER['PATH_INFO'] = $_path; // I didn't notice sfRouting sets currentRouteName when it parses url, so ... $tempReq->initialize(sfContext::getInstance()); return $result; }
This function doesn't check whether the url belongs to the current application, as sfWebRequest doesn't do that.
This is how to create a complex grouping using doctrine query.
select ... from ... where x = 1 and (a in(1,2,3) or a is not null)
class myDQ extends Doctrine_Query { public static function create($conn = null) { return new myDQ($conn); } public function toGroup() { $where = $this->_dqlParts['where']; if (count($where) > 0) { array_splice($where, count($where) - 1, 0, '('); $this->_dqlParts['where'] = $where; } return $this; } public function endGroup() { $where = $this->_dqlParts['where']; if (count($where) > 0) { $where[] = ')'; $this->_dqlParts['where'] = $where; } return $this; } }
$q = myDQ::create() ->from('Some s') ->where('s.x = ?', 1) ->andWhereIn('s.a', array(1,2,3)) ->toGroup() ->orWhere('s.a is not null') ->endGroup();
Yeah, so am I.
Here's how to create a "sf" command in your terminal that will do exactly the same job than when you use the "php symfony xxx" command.
There's many ways to do it, here's mine, it should works on all unix systems.
In the "/usr/bin" directory, create an "sf" file with this content :
#!/usr/bin/env php <?php // This file provides the cli "sf" shortcut instead of "php symfony" in syfony projects if (file_exists('config/ProjectConfiguration.class.php')) { require_once('config/ProjectConfiguration.class.php'); $dir = sfCoreAutoload::getInstance()->getBaseDir(); } else { die('You must be in a symfony project directory.'."\n"); } include($dir.'/command/cli.php');
Be carefull, don't forget the first line !
Now, open the rights on execution for the file: "sudo chmod +x /usr/bin/sf"
That's it, you're done. Go in a symfony project and try your brand new "sf" command in your terminal.
May the laziness be with you !
[UPDATE]
Pedro Casado has reported in comments a cleaner method that uses the native bash configuration file :
It worked pretty great for me.
[/UPDATE]