I need to execute Symfony command quite frequently (like every 5-10 seconds) but my runtime environement restricts the lowest time possible of cron trigger to 1 minute. So, I've come up with a pretty nasty solution, looks like this. I've had created AbstractRepetitiveCommand class, which I then extended, providing implemenation of workToExecute method.
abstract class AbstractRepetitiveCommand extends Command
{
protected int $overallMinutes = 1;
protected int $timesPerMinute = 60;
protected function execute(InputInterface $input, OutputInterface $output)
{
$startTime = microtime(true);
while (microtime(true) < $startTime + 60 * $this->overallMinutes - 60 / $this->timesPerMinute) {
if (isset($last) && microtime(true) < $last + 60 / $this->timesPerMinute) {
continue;
}
$last = microtime(true);
$this->workToExecute($input, $output);
}
}
protected function workToExecute(InputInterface $input, OutputInterface $output)
{
return;
}
}
class DoChecksCommand extends AbstractRepetitiveCommand
{
protected int $overallMinutes = 5;
protected int $timePerMinute = 4;
protected static $defaultName = 'checks:do';
/**
* #var InvestmentCheckerService
*/
private InvestmentCheckerService $checkerService;
/**
* #var EntityManagerInterface
*/
private EntityManagerInterface $entityManager;
public function __construct(InvestmentCheckerService $checkerService, EntityManagerInterface $entityManager)
{
parent::__construct('DoChecks');
$this->checkerService = $checkerService;
$this->entityManager = $entityManager;
}
/**
* #param InputInterface $input
* #param OutputInterface $output
* #throws \Exception
*/
protected function workToExecute(InputInterface $input, OutputInterface $output)
{
/** #var ArrayCollection | Pair[] $pairs */
$pairs = $this->entityManager->getRepository(Pair::class)->findAll();
foreach ($pairs as $pair) {
$this->checkerService->processChecks($pair);
$this->entityManager->persist($pair);
$this->entityManager->flush();
}
}
}
There are two problems with this approach:
When 2 o this kind of commands run simultanously, they don't
recognize each other's changes made by flush. Is there a way to
refresh connection, so with every run the data is fetched from
database?
This solution is really ugly overall. I'm probably
overthinking/overcomplicating it, so please advise whether you see any
better approach to the goal I want to achieve.
For now I've tried to use clear() method of EntityManager at the start of every loop but it didn't change anything. I thought about closing the connection, but then I don't see any easy way to reconnect again after closing. Thank you for any help.
For now I've tried to use clear() method of EntityManager at the start of every loop but it didn't change anything.
That's exactly what you need to do. With EntityManager cleared Doctrine will issue a database query to fetch fresh data, so any change you made in other commands/request will be available to you. The only case when it wouldn't be true is unclosed transactions but I see no explicit transactions in your code so everything should be fine.
Related
i was looking at the source code of laravel cashier and saw this:
/**
* Get a subscription instance by name.
*
* #param string $subscription
* #return \Laravel\Cashier\Subscription|null
*/
public function subscription($subscription = 'default')
{
return $this->subscriptions->sortByDesc(function ($value) {
return $value->created_at->getTimestamp();
})
->first(function ($value) use ($subscription) {
return $value->name === $subscription;
});
}
why the 2 anonymous functions? why not just (bit naive):
$this->subscriptions->where('name', $value)
In a custom extbase extension, I need to call a show action, passing it another value than uid (this is a continuation of Use other than primary key as RealURL id_field).
As the value "other than uid" is not resolved, it results in the exception http://wiki.typo3.org/Exception/CMS/1297759968
EDIT: here's the current working (but ugly) Controller code:
/**
* ItemController
*/
class ItemController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionController {
/**
* itemRepository
*
* #var \STUBR\Weiterbildung\Domain\Repository\ItemRepository
* #inject
*/
protected $itemRepository = NULL;
/**
* action list
*
* #return void
*/
public function listAction(\STUBR\Weiterbildung\Domain\Model\Item $item=null) {
if (!empty(\TYPO3\CMS\Core\Utility\GeneralUtility::_GET('tx_weiterbildung_pi1'))){
$this->forward('show');
}
$items = $this->itemRepository->findAll();
$this->view->assign('items', $items);
}
/**
* action show
*
* #param \STUBR\Weiterbildung\Domain\Model\Item $item
* #return void
*/
public function showAction(\STUBR\Weiterbildung\Domain\Model\Item $item=null) {
$tx_weiterbildung_pi1_get = \TYPO3\CMS\Core\Utility\GeneralUtility::_GET('tx_weiterbildung_pi1');
if (!empty($tx_weiterbildung_pi1_get)){
$dfid = intval($tx_weiterbildung_pi1_get['durchfuehrungId']);
}
$items = $this->itemRepository->findByDurchfuehrungId($dfid);
// helpful:
//\TYPO3\CMS\Extbase\Utility\DebuggerUtility::var_dump($item);
$this->view->assign('item', $items[0]);
}
}
PS here are the 5 lines that could be used in the Repository if the magical method wouldn't work:
//public function findByDurchfuehrungId($dfid) {
// $query = $this->createQuery();
// $query->matching($query->equals('durchfuehrungId', $dfid));
// return $query->execute();
//}
Yes, when you're using actions with model binding in param it will always look for object by field defined as ID - in our case it's always uid, but remember, that you do not need to bind model automatically, as you can retrive it yourself.
Most probably you remember, that some time ago I advised to use <f:link.page additionalParams="{originalUid:myObj.originalUid}"Click here</f:link.page> instead of <f:link.action...>
In that case your show action would look like this:
public function showAction() {
$item = $this->itemRepository->findByOriginalUid(intval(GeneralUtility::_GET('originalUid')));
$this->view->assign('item', $item);
}
Where findByOriginalUid should work magically without declaration, but even if it doesn't it's just matter of 5 lines in the repo ;)
Other sample
According to the code you pasted I'd use it rather like this, in this case listAction gets a role of dispatcher for whole plugin:
public function listAction() {
// access get param in array
$tx_weiterbildung_pi1_get = \TYPO3\CMS\Core\Utility\GeneralUtility::_GET('tx_weiterbildung_pi1');
$dfId = intval($tx_weiterbildung_pi1_get['durchfuehrungId']);
if ($dfId > 0) { // tx_weiterbildung_pi1 exists and is positive, that means you want to go to showAction
$item = $this->itemRepository->findByDurchfuehrungId($dfId);
if (!is_null($item)) { // Forward to showAction with found $item
$this->forward('show', null, null, array('item' => $item));
}else { // Or redirect to the view URL after excluding single item ID from GET params
$this->redirectToUri($this->uriBuilder->setArgumentsToBeExcludedFromQueryString(array('tx_weiterbildung_pi1'))->build());
}
}
// No `tx_weiterbildung_pi1` param, so it should be displayed as a listAction
$items = $this->itemRepository->findAll();
$this->view->assign('items', $items);
}
/**
* #param \STUBR\Weiterbildung\Domain\Model\Item $item
*/
public function showAction(\STUBR\Weiterbildung\Domain\Model\Item $item = null) {
$this->view->assign('item', $item);
}
Your finder should also getFirst() object if possible:
public function findByDurchfuehrungId($DfId) {
$query = $this->createQuery();
$query->matching($query->equals('durchfuehrungId', $DfId));
return $query->execute()->getFirst();
}
I'm having the following situation in doctrine ODM with MongoDB and Symfony 2.x
Class A:
class Port extends AbstractEthernetPort
{
/** Other Fields **/
/**
* Owning the reference
* #MongoDB\ReferenceOne(
* targetDocument="\xxx\AbstractObject",
* cascade="all",
* inversedBy="ports"
* )
*/
protected $device;
/** SETTER and GETTERS **/
}
Class B:
class Device extends AbstractObject
{
/** Other Fields **/
/**
* #MongoDB\ReferenceMany(
* targetDocument="\xxx\AbstractEthernetPort",
* cascade="all",
* mappedBy="device"
* )
*/
protected $ports = array();
/** SETTER and GETTERS **/
}
These both classes are linked togehter with ReferenceOne and ReferenceMany. The code has beend slightly changed for this post.
This are the both versions of the testcase. The First does not work, the second does:
public function testPorts() {
$dm = self::$container->get('doctrine_mongodb')->getManager();
$sideASwitch = new Device();
$sideASwitch->setName("Switch01");
$copper1 = new Port();
$copper1->setDescription("Copper Port");
$copper2 = new Port();
$copper2->setDescription("Copper Port");
$sideASwitch->setPorts(array($copper1, $copper2));
$dm->persist($sideASwitch);
$dm->flush();
$x = $dm->getRepository("Device")->findOneBy(array());
\Doctrine\Common\Util\Debug::dump($x,1);
}
The query at the end returns an ports array with 0 content.
public function testPorts() {
$dm = self::$container->get('doctrine_mongodb')->getManager();
$sideASwitch = new Device();
$sideASwitch->setName("Switch01");
$copper1 = new Port();
$copper1->setDescription("Copper Port");
$copper2 = new Port();
$copper2->setDescription("Copper Port");
// ADDITIONAL
$copper1->setDevice($sideASwitch);
$copper2->setDevice($sideASwitch);
$sideASwitch->setPorts(array($copper1, $copper2));
$dm->persist($sideASwitch);
$dm->flush();
$x = $dm->getRepository("Device")->findOneBy(array());
\Doctrine\Common\Util\Debug::dump($x,1);
}
This Query returns an ports array with 2 objects in it...
Is this the normal behaviour in Doctrine ODM or are am i doing something wrong?
Thanks for any help
This is the expected behavior. Calling $sideASwitch->setPorts(array($copper1, $copper2)); has no effect because ports is the mapped side.
As a convenience, I often do something like the following on the mapped (Device) side:
public function setPorts(array $ports)
{
foreach($ports as $port) {
$port->setDevice($this);
}
return $this;
}
This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 11 years ago.
This is basically the "Am i doing it right?" question.
I have an idea how i can transparently switch views for default/mobile version/admin areas at run time. And I would like to know what pros and cons you see in this approach.
Main requirements are:
switch entire application with
little to no coding
integrate into zend MVC workflow,
not overwrite it
fallback to default
preserve standard functionality
controllers shouldn't be aware of
changes
Here is my pseudohelper
class Xrks_Controller_Action_Helper_VrExtension extends Zend_Controller_Action_Helper_Abstract
{
public function postDispatch()
{
if(!$this->_shouldRender()) {
return; //just skip
}
try {
$vr = $this->_getViewRenderer();
$backupView = clone $vr->view;
$this->_setBasePaths(); //set base path(s) through ViewRenderer::initView($path)
$oldSpecArray = $this->_setVrPathSpecs(); //set VR view script path specs
$vr->render();
$vr->setNoRender(true); //disable renderer
} catch(Zend_View_Exception $e) { //fallback to default viewscripts if view script file not found
$vr->setView($backupView); //restore view on error
} catch(Exception $e) {
$vr->setView($backupView); //restore view on error
$this->_setVrPathSpecs($oldSpecArray); //restore script path spec
throw $e;
}
$this->_setVrPathSpecs($oldSpecArray);//restore script path spec
}
/**
* Same functionality as ViewRenderer helper _shouldRender method
* #return boolean
*/
protected function _shouldRender();
/**
* #return Zend_Controller_Action_Helper_ViewRenderer
*/
protected function _getViewRenderer();
/**
* Sets viewRenderer path specifications
*
* #param array $spec if NULL uses $this->_viewRendererPathSpecs
* #return array old path spec (0 => pathSpec, 1 => pathNoControllerSpec)
*/
protected function _setVrPathSpecs(array $spec = NULL);
}
How exactly helper should be configured is not important and that part skipped
Here is example how it supposed to work:
$this->_setBasePaths(); sets view base paths to application/views/default/ and application/views/admin/
$this->_setVrPathSpecs(); set path specification to ':module/:controller/:action.:suffix'
so for foo-baz-bar it will search at
1. application/views/admin/scripts/foo/baz/bar.phtml
2. application/views/default/scripts/foo/baz/bar.phtml
if view script not found fall back to default ViewRenderer:
3. application/modules/foo/views/scripts/baz/bar.phtml
Ask questions if I missed something
Upd: After some research i decided to use action helper to autoregister view scriptPaths based on specification for inflector and specified variables. I also modified partial helpers to register scriptPaths if partial from other module requested.
This is crude but working version of action helper:
class Xrks_Controller_Action_Helper_ViewRendererPathstack extends Zend_Controller_Action_Helper_Abstract
{
const PATH_APPEND = 'append';
const PATH_PREPEND = 'prepend';
protected $_enabled = FALSE;
protected $_viewScriptPaths = array();
/**
* By default following vars available: baseDir, area, theme, module
* #var string
*/
protected $_viewScriptPathSpec = ':baseDir/:area/:module';
protected $_defaults = array(
'area' => 'frontend',
'theme' => 'default',
);
protected $_vars = array();
protected $_inflector;
protected $_viewRenderer;
public function __construct($baseDir = NULL)
{
if($baseDir == NULL) {
$baseDir = APPLICATION_PATH . DS . 'views';
}
$this->setDefaultVar('baseDir', $baseDir);
$this->addPath(array());
}
/**
* Enter description here ...
* #return Zend_Controller_Action_Helper_ViewRenderer
*/
protected function _getViewRenderer()
{
if(!$this->_viewRenderer) {
$this->_viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
}
return $this->_viewRenderer;
}
/**
* Should the ViewRenderer render a view script?
*
* #return boolean
*/
protected function _shouldRender()
{
$vR = $this->_getViewRenderer();
return (!$this->getFrontController()->getParam('noViewRenderer')
&& !$vR->getNeverRender()
&& !$vR->getNoRender()
&& (null !== $vR->getActionController())
&& $vR->getRequest()->isDispatched()
&& !$vR->getResponse()->isRedirect()
);
}
public function generatePaths(array $vars = array())
{
$this->_registerVarsWithInflector();
$vars = array_merge($this->_defaults, $this->_vars, $vars);
$inflector = $this->getInflector();
$generatedPaths = array();
foreach($this->_viewScriptPaths as $path) {
$pathVars = array_merge($vars, $path);
$generatedPaths[] = $inflector->filter($pathVars);
}
return array_reverse(array_unique(array_reverse($generatedPaths)));//last occurence more important than first
// array('test', 'test2', 'test') => array('test2', 'test')
// #todo rethink this code piece later. must be better solution
}
protected function _registerVarsWithInflector()
{
$vars = array_merge($this->_defaults, $this->_vars);
$inflector = $this->getInflector();
$unregistered = array_keys(array_diff_key($vars, $inflector->getRules()));
sort($unregistered, SORT_DESC);//more specific first (moduleDir prior to module key)
foreach($unregistered as $var) {
$inflector->addFilterRule($var, array('Word_CamelCaseToDash', 'StringToLower'));
}
}
protected function _viewAddScriptPaths(Zend_View_Abstract $view, $paths)
{
foreach ($paths as $path) {
$view->addScriptPath($path);
}
}
/**
* Get inflector
*
* #return Zend_Filter_Inflector
*/
public function getInflector()
{
if (null === $this->_inflector) {
$this->_inflector = new Zend_Filter_Inflector();
$this->_inflector->setThrowTargetExceptionsOn(true);
//setup default rules
$this->_inflector->addRules(array(
':baseDir' => array(),
))
->setTargetReference($this->_viewScriptPathSpec);
}
return $this->_inflector;
}
/**
*
* #return array
*/
public function getPaths()
{
return $this->_basePaths;
}
public function getEnabled()
{
return $this->_enabled;
}
public function setEnabled($flag = TRUE)
{
$this->_enabled = (bool)$flag;
return $this;
}
/**
*
* #todo add check for $pathVars keys and values validity
* #param array $pathVars associative array
* #param string $placement either append or prepend
* #return Xrks_Controller_Action_Helper_ViewRendererPathstack
*/
public function addPath(array $pathVars, $placement = self::PATH_APPEND)
{
if($placement == self::PATH_PREPEND) {
array_unshift($this->_viewScriptPaths, $pathVars);
} else {
$this->_viewScriptPaths[] = $pathVars;
}
return $this;
}
/**
*
* #param array|Zend_Config $paths
* #param string $placement either append or prepend
* #return Xrks_Controller_Action_Helper_ViewRendererPathstack
* #throws Xrks_Exception
*/
public function addPaths($paths, $placement = self::PATH_APPEND)
{
if($paths instanceof Zend_Config) {
$paths = $paths->toArray();
} elseif (!is_array($paths)) {
throw new Xrks_Exception('$paths should be either array or instance of Zend_Config');
}
if($placement == self::PATH_PREPEND) {
$paths = array_reverse($paths);
}
foreach($paths as $path) {
$this->addPath((array)$path, $placement);
}
return $this;
}
/**
*
* #param array $pathVars associative array
* #return Xrks_Controller_Action_Helper_ViewRendererPathstack
*/
public function setPath(array $pathVars)
{
$this->_basePaths = array();
$this->addPath($pathVars);
return $this;
}
/**
*
* #param array|Zend_Config $paths
* #return Xrks_Controller_Action_Helper_ViewRendererPathstack
* #throws Xrks_Exception
*/
public function setPaths($paths)
{
$this->_basePaths = array();
$this->addPaths($paths);
return $this;
}
/**
*
* #param string $varName
* #return string |NULL
*/
public function getDefaultVar($varName)
{
if(key_exists($varName, $this->_defaults)) {
return $this->_defaults[$varName];
}
return NULL;
}
/**
* #param string $varName
* #param string $value
* #return Xrks_Controller_Action_Helper_ViewRendererPathstack Provides fluent interface
*/
public function setDefaultVar($varName, $value)
{
$this->_defaults[$varName] = (string)$value;
return $this;
}
/**
*
* #param string $name
* #return string |NULL
*/
public function getVar($name, $defaults = false)
{
if(key_exists($name, $this->_vars)) {
return $this->_vars[$name];
}
return $defaults ? $this->getDefaultVar($name) : NULL;
}
/**
* #param string $varName
* #param string $value
* #return Xrks_Controller_Action_Helper_ViewRendererPathstack Provides fluent interface
*/
public function setVar($varName, $value)
{
$this->_vars[$varName] = $value;
return $this;
}
public function unsetVar($name)
{
if(key_exists($name, $this->_vars)) {
unset($this->_vars[$name]);
}
return $this;
}
public function postDispatch()
{
if(!$this->getEnabled() || !$this->_shouldRender()) {
return; //just skip
}
try {
$vr = $this->_getViewRenderer();
$this->setVar('module', $vr->getModule());
$paths = $this->generatePaths();
$this->_viewAddScriptPaths($vr->view, $paths);
if(Zend_Registry::isRegistered('Zend_Log')) {
Zend_Registry::get('Zend_Log')
->log($paths, Zend_Log::DEBUG);
}
} catch(Exception $e) {
if(Zend_Registry::isRegistered('Zend_Log')) {
Zend_Registry::get('Zend_Log')
->log($e, Zend_Log::WARN);
}
throw $e;
}
}
}
The way I usually handle this:
I register a Layout Plugin, extending Zend_Layout_Controller_Plugin_Layout
I use the preDispatch hook to determine what module, controller, action I am in
I switch between layouts and views depending on the context
For me, that's by far the easiest method.
GJ
Lets assume the following code within a controller:
$this->view->addScriptPath('dir1/views/scripts');
$this->view->addScriptPath('dir2/views/scripts');
$this->render('index.phtml');
Where dir1/views/scripts contains 2 files:
-index.phtml
-table.phtml
And dir2/views/scripts:
-table.phtml
Now, it will render the index.phtml in dir1 since dir 2 doesn't have an index.phtml.
Index.phtml looks something like:
<somehtml>
<?= $this->render('table.phtml') ?>
</somehtml>
This is where the confusion starts for me. I would expect it to render the table.phtml in the last directory added to the script path stack, but it doesn't.
Is there a simple solution/explanation to my problem?
Seems that paths are used in LIFO order.
Take a look at viewRednderer and view source files to see how does it work.
u can use
> $this->view->setBasePath("../application/dir1/views");
that is more specific
I ended up extending Zend_View and adding the function renderParent:
class My_View extends Zend_View
{
private $_file = null;
private $_name = null;
/**
* Finds a view script from the available directories.
*
* #param $name string The base name of the script.
* #return void
*/
protected function _script($name)
{
$this->_file = parent::_script($name);
$this->_name = $name;
return $this->_file;
}
/**
* Renders the parent script by looping through all the script paths.
*
* #return void
*/
public function renderParent()
{
$scriptPaths = $this->getScriptPaths();
$found = false;
for ($i = 0; $i < count($scriptPaths); $i++) {
if ($this->_file == $scriptPaths[$i] . $this->_name) {
$found = true;
} elseif ($found) {
if (is_readable($scriptPaths[$i] . $this->_name)) {
return $this->_run($scriptPaths[$i] . $this->_name);
}
}
}
}
}