├── .bootstrap.php.dist ├── .gitignore ├── Command ├── RestartCommand.php ├── RunCommand.php ├── StartCommand.php ├── StopCommand.php ├── Test1Command.php ├── Test2Command.php ├── Test3Command.php └── Test4Command.php ├── Controller ├── DefaultController.php ├── HistoryController.php ├── JobController.php └── ScheduledJobController.php ├── DependencyInjection ├── Configuration.php └── NineThousandJobqueueExtension.php ├── Entity ├── Arg.php ├── History.php ├── Job.php ├── Param.php └── Tag.php ├── Form ├── ArgType.php ├── JobType.php ├── ParamType.php ├── ScheduledJobType.php └── TagType.php ├── NineThousandJobqueueBundle.php ├── README.md ├── Repository └── JobRepository.php ├── Resources ├── config │ ├── example.config.yml │ ├── jobqueue.xml │ ├── routing.xml │ └── routing │ │ ├── history.xml │ │ ├── job.xml │ │ └── scheduledjob.xml ├── public │ ├── css │ │ ├── smoothness │ │ │ ├── images │ │ │ │ ├── ui-bg_flat_0_aaaaaa_40x100.png │ │ │ │ ├── ui-bg_flat_75_ffffff_40x100.png │ │ │ │ ├── ui-bg_glass_55_fbf9ee_1x400.png │ │ │ │ ├── ui-bg_glass_65_ffffff_1x400.png │ │ │ │ ├── ui-bg_glass_75_dadada_1x400.png │ │ │ │ ├── ui-bg_glass_75_e6e6e6_1x400.png │ │ │ │ ├── ui-bg_glass_95_fef1ec_1x400.png │ │ │ │ ├── ui-bg_highlight-soft_75_cccccc_1x100.png │ │ │ │ ├── ui-icons_222222_256x240.png │ │ │ │ ├── ui-icons_2e83ff_256x240.png │ │ │ │ ├── ui-icons_454545_256x240.png │ │ │ │ ├── ui-icons_888888_256x240.png │ │ │ │ └── ui-icons_cd0a0a_256x240.png │ │ │ └── jquery-ui-1.8.13.custom.css │ │ └── style.css │ ├── favicon.ico │ ├── images │ │ └── gear-icon.png │ └── javascripts │ │ ├── jobqueue.js │ │ ├── jquery-1.5.1.min.js │ │ ├── jquery-ui-1.8.13.custom.min.js │ │ └── jquery.livequery.js └── views │ ├── Default │ └── index.html.twig │ ├── History │ └── index.html.twig │ ├── Job │ ├── index.html.twig │ ├── new.html.twig │ └── show.html.twig │ ├── ScheduledJob │ ├── index.html.twig │ ├── new.html.twig │ └── show.html.twig │ ├── ajax.html.twig │ ├── footer.html.twig │ ├── layout.html.twig │ ├── menu.html.twig │ └── pagination.html.twig ├── Tests ├── Controller │ ├── ArgControllerTest.php │ ├── JobControllerTest.php │ ├── ParamControllerTest.php │ └── TagControllerTest.php ├── Fixtures │ ├── History │ │ ├── LoadHistoryTestData.php │ │ └── LoadStandardHistoryTestData.php │ ├── Job │ │ ├── LoadJobTestData.php │ │ └── LoadJobqueueControlTestData.php │ └── LoadTestData.php ├── History │ └── StandardHistoryTest.php ├── Service │ └── JobqueueControlTest.php └── Util │ └── CronParserTest.php ├── Vendor └── Doctrine │ └── Adapter │ └── Job │ └── Symfony2DoctrineJobAdapter.php └── phpunit.xml.dist /.bootstrap.php.dist: -------------------------------------------------------------------------------- 1 | setName('jobqueue:restart') 19 | ->setDescription('Stops the jobqueue daemon') 20 | ->setHelp(<<{$this->getName()} Restarts the Jobqueue daemon running in the background. 22 | EOT 23 | ); 24 | } 25 | 26 | protected function execute(InputInterface $input, OutputInterface $output) 27 | { 28 | $daemon = new Daemon($this->getContainer()->getParameter('jobqueue.daemon.options')); 29 | 30 | $daemon->reStart(); 31 | 32 | while ($daemon->isRunning()) { 33 | $this->getContainer()->get('jobqueue.control')->run(); 34 | } 35 | 36 | $daemon->stop(); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /Command/RunCommand.php: -------------------------------------------------------------------------------- 1 | setName('jobqueue:run') 17 | ->setDescription('Cycles through the jobqueue only once') 18 | ->setHelp(<<{$this->getName()} Run the Jobqueue one time through. 20 | EOT 21 | ); 22 | } 23 | 24 | protected function execute(InputInterface $input, OutputInterface $output) 25 | { 26 | $this->getContainer()->get('jobqueue.control')->run(); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /Command/StartCommand.php: -------------------------------------------------------------------------------- 1 | setName('jobqueue:start') 19 | ->setDescription('Starts the jobqueue daemon') 20 | ->setHelp(<<{$this->getName()} Run the Jobqueue daemon in the background. 22 | EOT 23 | ); 24 | } 25 | 26 | protected function execute(InputInterface $input, OutputInterface $output) 27 | { 28 | $daemon = new Daemon($this->getContainer()->getParameter('jobqueue.daemon.options')); 29 | $daemon->start(); 30 | 31 | while ($daemon->isRunning()) { 32 | $this->getContainer()->get('jobqueue.control')->run(); 33 | } 34 | 35 | $daemon->stop(); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /Command/StopCommand.php: -------------------------------------------------------------------------------- 1 | setName('jobqueue:stop') 19 | ->setDescription('Stops the jobqueue daemon') 20 | ->setHelp(<<{$this->getName()} Stop the Jobqueue daemon from running in the background. 22 | EOT 23 | ); 24 | } 25 | 26 | protected function execute(InputInterface $input, OutputInterface $output) 27 | { 28 | $daemon = new Daemon($this->getContainer()->getParameter('jobqueue.daemon.options')); 29 | $daemon->stop(); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /Command/Test1Command.php: -------------------------------------------------------------------------------- 1 | setName('jobqueue:test1') 19 | ->setDescription('Runs a Test of the jobqueue bundle') 20 | ->setHelp(<<{$this->getName()} Will output to the log upon successful completion. 22 | EOT 23 | ); 24 | } 25 | 26 | protected function execute(InputInterface $input, OutputInterface $output) 27 | { 28 | $this->getContainer()->get('logger')->debug('Command 1 Successfully Ran.'); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /Command/Test2Command.php: -------------------------------------------------------------------------------- 1 | setName('jobqueue:test2') 19 | ->setDescription('Runs a Test of the jobqueue bundle') 20 | ->setHelp(<<{$this->getName()} Will output to the log upon successful completion. 22 | EOT 23 | ); 24 | } 25 | 26 | protected function execute(InputInterface $input, OutputInterface $output) 27 | { 28 | $this->getContainer()->get('logger')->debug('Command 2 Successfully Ran.'); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /Command/Test3Command.php: -------------------------------------------------------------------------------- 1 | setName('jobqueue:test3') 19 | ->setDescription('Runs a Test of the jobqueue bundle') 20 | ->setHelp(<<{$this->getName()} Will output to the log upon successful completion. 22 | EOT 23 | ); 24 | } 25 | 26 | protected function execute(InputInterface $input, OutputInterface $output) 27 | { 28 | $this->getContainer()->get('logger')->debug('Command 3 Successfully Ran.'); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /Command/Test4Command.php: -------------------------------------------------------------------------------- 1 | setName('jobqueue:test4') 19 | ->setDescription('Runs a Test of the jobqueue bundle') 20 | ->setHelp(<<{$this->getName()} Will output to the log upon successful completion. 22 | EOT 23 | ); 24 | } 25 | 26 | protected function execute(InputInterface $input, OutputInterface $output) 27 | { 28 | $this->getContainer()->get('logger')->debug('Command 4 Successfully Ran.'); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /Controller/DefaultController.php: -------------------------------------------------------------------------------- 1 | get('jobqueue.control'); 14 | 15 | return $this->render('NineThousandJobqueueBundle:Default:index.html.twig', array( 16 | 'activeQueue' => $queueControl->getActiveQueue(), 17 | 'retryQueue' => $queueControl->getRetryQueue(), 18 | 'scheduleQueue' => $queueControl->getScheduleQueue(), 19 | )); 20 | 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Controller/HistoryController.php: -------------------------------------------------------------------------------- 1 | container->getParameter('jobqueue.adapter.options'); 22 | $uiOptions = $this->container->getParameter('jobqueue.ui.options'); 23 | 24 | $historyAdapterClass = $queueOptions['history_adapter_class']; 25 | $historyAdapter = new $historyAdapterClass($this->container->getParameter('jobqueue.adapter.options'), 26 | $this->container->get('doctrine')->getEntityManager()); 27 | $query = array(); 28 | $pageParam = 'hpage'; 29 | $query['page'] = $this->getRequest()->query->get($pageParam) ?: 1; 30 | $query['reverse'] = $this->getRequest()->query->get('reverse') ?: null; 31 | $query['job'] = $this->getRequest()->query->get('job') ?: null; 32 | $offset = ($uiOptions['pagination']['limit'] * $query['page']) - $uiOptions['pagination']['limit']; 33 | $history = new History($historyAdapter, $uiOptions['pagination']['limit'], $offset, $query['reverse'], $query['job']); 34 | $pagination = $this->getPagination($query, $uiOptions['pagination'], $history->getTotal(), $pageParam); 35 | 36 | return $this->render('NineThousandJobqueueBundle:History:index.html.twig', array( 37 | 'history' => $history, 38 | 'pagination' => $pagination, 39 | )); 40 | } 41 | 42 | public function getPagination($query, $options, $total, $param) { 43 | 44 | $pages = array(); 45 | $current = $query['page']; 46 | unset($query['page']); 47 | 48 | $query = ($query = http_build_query($query)) ? '&'.$query : ''; 49 | $pCount = floor($total / $options['limit']); 50 | if ($remainder = $total % $options['limit']) { 51 | $pCount++; 52 | } 53 | $start = (($n = $current-$options['pages_before']) > 1) ? $n : 1; 54 | $end = (($m = $current+$options['pages_after']) < $pCount) ? $m : $pCount; 55 | for ($i=$start;$i<=$end;$i++) { 56 | array_push($pages, $i); 57 | } 58 | 59 | return array( 60 | 'current' => $current, 61 | 'pages' => $pages, 62 | 'last' => $pCount, 63 | 'param' => $param, 64 | 'query' => $query, 65 | ); 66 | } 67 | 68 | } 69 | 70 | 71 | -------------------------------------------------------------------------------- /Controller/JobController.php: -------------------------------------------------------------------------------- 1 | container->getParameter('jobqueue.ui.options'); 30 | $pageParam = 'jpage'; 31 | $query['limit'] = $uiOptions['pagination']['limit']; 32 | $query['page'] = $this->getRequest()->query->get($pageParam) ?: 1; 33 | $query['status'] = $this->getRequest()->query->get('status') ?: null; 34 | $query['scheduled'] = $this->getRequest()->query->get('scheduled') ?: null; 35 | $query['reverse'] = $this->getRequest()->query->get('reverse') ?: null; 36 | $query['offset'] = ($query['limit'] * $query['page']) - $query['limit']; 37 | 38 | $em = $this->getDoctrine()->getEntityManager(); 39 | 40 | $result = $em->getRepository('NineThousandJobqueueBundle:Job')->findAllByQuery($query); 41 | 42 | $pagination = $this->getPagination($query, $uiOptions['pagination'], $result['totalResults'], $pageParam); 43 | $forms = array(); 44 | foreach ($result['entities'] as $entity) { 45 | $id = $entity->getId(); 46 | $forms[$id]['deactivate_form'] = $this->createDeactivateForm()->createView(); 47 | $forms[$id]['activate_form'] = $this->createActivateForm()->createView(); 48 | $forms[$id]['retry_form'] = $this->createRetryForm()->createView(); 49 | } 50 | 51 | return $this->render('NineThousandJobqueueBundle:Job:index.html.twig', array( 52 | 'entities' => $result['entities'], 53 | 'forms' => $forms, 54 | 'pagination' => $pagination, 55 | )); 56 | } 57 | 58 | /** 59 | * Finds and displays a Job entity. 60 | * 61 | */ 62 | public function showAction($id) 63 | { 64 | $em = $this->getDoctrine()->getEntityManager(); 65 | 66 | $entity = $em->getRepository('NineThousandJobqueueBundle:Job')->find($id); 67 | 68 | if (!$entity) { 69 | throw $this->createNotFoundException('Unable to find Job entity.'); 70 | } 71 | 72 | $deactivateForm = $this->createDeactivateForm($id); 73 | $activateForm = $this->createActivateForm($id); 74 | $retryForm = $this->createRetryForm($id); 75 | 76 | return $this->render('NineThousandJobqueueBundle:Job:show.html.twig', array( 77 | 'entity' => $entity, 78 | 'deactivate_form' => $deactivateForm->createView(), 79 | 'activate_form' => $activateForm->createView(), 80 | 'retry_form' => $retryForm->createView(), 81 | )); 82 | } 83 | 84 | /** 85 | * Displays a form to create a new Job entity. 86 | * 87 | */ 88 | public function newAction() 89 | { 90 | $entity = new Job(); 91 | $entity->setParams(array(new Param())); 92 | $entity->setArgs(array(new Arg())); 93 | $entity->setTags(array(new Tag())); 94 | $form = $this->createForm(new JobType(), $entity, $this->container->getParameter('jobqueue.adapter.options')); 95 | 96 | return $this->render('NineThousandJobqueueBundle:Job:new.html.twig', array( 97 | 'entity' => $entity, 98 | 'form' => $form->createView(), 99 | 'action' => $this->generateUrl('jobqueue_job_create'), 100 | )); 101 | } 102 | 103 | /** 104 | * Creates a new Job entity. 105 | * 106 | */ 107 | public function createAction() 108 | { 109 | $entity = new Job(); 110 | $request = $this->getRequest(); 111 | $form = $this->createForm(new JobType(), $entity, $this->container->getParameter('jobqueue.adapter.options')); 112 | 113 | if ('POST' === $request->getMethod()) { 114 | 115 | $this->sanitizeCollections($form, $request, array( 116 | 'params' => array('key','value'), 117 | 'args' => array('value'), 118 | 'tags' => array('value'), 119 | )); 120 | 121 | $form->bindRequest($request); 122 | 123 | if ($form->isValid()) { 124 | $em = $this->getDoctrine()->getEntityManager(); 125 | $this->setCollectionInverse($em, $entity, array( 126 | 'params' => 'job', 127 | 'args' => 'job', 128 | )); 129 | $entity->setCreateDate(new \DateTime); 130 | $entity->setActive(1); 131 | $em->persist($entity); 132 | $em->flush(); 133 | 134 | return $this->redirect($this->generateUrl('jobqueue_job_show', array( 135 | 'id' => $entity->getId(), 136 | ))); 137 | 138 | } 139 | } 140 | 141 | return $this->render('NineThousandJobqueueBundle:Job:new.html.twig', array( 142 | 'entity' => $entity, 143 | 'form' => $form->createView(), 144 | 'action' => $this->generateUrl('jobqueue_job_create'), 145 | )); 146 | } 147 | 148 | /** 149 | * Displays a form to edit an existing Job entity. 150 | * 151 | */ 152 | public function editAction($id) 153 | { 154 | $em = $this->getDoctrine()->getEntityManager(); 155 | 156 | $entity = $em->getRepository('NineThousandJobqueueBundle:Job')->find($id); 157 | 158 | if (!$entity) { 159 | throw $this->createNotFoundException('Unable to find Job entity.'); 160 | } 161 | 162 | if (count($entity->getParams()) < 1) { 163 | $entity->setParams(array(new Param())); 164 | } 165 | 166 | if (count($entity->getArgs()) < 1) { 167 | $entity->setArgs(array(new Arg())); 168 | } 169 | 170 | if (count($entity->getTags()) < 1) { 171 | $entity->setTags(array(new Tag())); 172 | } 173 | 174 | $form = $this->createForm(new JobType(), $entity, $this->container->getParameter('jobqueue.adapter.options')); 175 | $deactivateForm = $this->createDeactivateForm(); 176 | $activateForm = $this->createActivateForm(); 177 | $retryForm = $this->createRetryForm(); 178 | 179 | return $this->render('NineThousandJobqueueBundle:Job:new.html.twig', array( 180 | 'entity' => $entity, 181 | 'form' => $form->createView(), 182 | 'deactivate_form' => $deactivateForm->createView(), 183 | 'activate_form' => $activateForm->createView(), 184 | 'retry_form' => $retryForm->createView(), 185 | 'action' => $this->generateUrl('jobqueue_job_update', array('id' => $id)), 186 | )); 187 | } 188 | 189 | /** 190 | * Edits an existing Job entity. 191 | * 192 | */ 193 | public function updateAction($id) 194 | { 195 | $em = $this->getDoctrine()->getEntityManager(); 196 | 197 | $entity = $em->getRepository('NineThousandJobqueueBundle:Job')->find($id); 198 | 199 | if (!$entity) { 200 | throw $this->createNotFoundException('Unable to find Job entity.'); 201 | } 202 | 203 | $form = $this->createForm(new JobType(), $entity, $this->container->getParameter('jobqueue.adapter.options')); 204 | $deactivateForm = $this->createDeactivateForm(); 205 | $activateForm = $this->createActivateForm(); 206 | $retryForm = $this->createRetryForm(); 207 | 208 | $request = $this->getRequest(); 209 | 210 | if ('POST' === $request->getMethod()) { 211 | $this->sanitizeCollections($form, $request, array( 212 | 'params' => array('key','value'), 213 | 'args' => array('value'), 214 | 'tags' => array('value'), 215 | )); 216 | 217 | $form->bindRequest($request); 218 | 219 | if ($form->isValid()) { 220 | $em = $this->getDoctrine()->getEntityManager(); 221 | $this->setCollectionInverse($em, $entity, array( 222 | 'params' => 'job', 223 | 'args' => 'job', 224 | )); 225 | $em->persist($entity); 226 | $em->flush(); 227 | 228 | return $this->redirect($this->generateUrl('jobqueue_job_edit', array('id' => $id))); 229 | } 230 | } 231 | 232 | return $this->render('NineThousandJobqueueBundle:Job:new.html.twig', array( 233 | 'entity' => $entity, 234 | 'form' => $form->createView(), 235 | 'deactivate_form' => $deactivateForm->createView(), 236 | 'activate_form' => $activateForm->createView(), 237 | 'retry_form' => $retryForm->createView(), 238 | 'action' => $this->generateUrl('jobqueue_job_update', array('id' => $id)), 239 | )); 240 | } 241 | 242 | /** 243 | * Makes a Job entity inactive. 244 | * 245 | */ 246 | public function deactivateAction($id) 247 | { 248 | $form = $this->createDeactivateForm($id); 249 | $request = $this->getRequest(); 250 | 251 | if ('POST' === $request->getMethod()) { 252 | $form->bindRequest($request); 253 | 254 | if ($form->isValid()) { 255 | $em = $this->getDoctrine()->getEntityManager(); 256 | $entity = $em->getRepository('NineThousandJobqueueBundle:Job')->find($id); 257 | 258 | if (!$entity) { 259 | throw $this->createNotFoundException('Unable to find Job entity.'); 260 | } 261 | 262 | $entity->setActive(0); 263 | $entity->setRetry(0); 264 | $entity->setSchedule(NULL); 265 | $em->persist($entity); 266 | $em->flush(); 267 | } 268 | } 269 | 270 | return $this->redirect($this->generateUrl('jobqueue_job_edit', array('id' => $id))); 271 | } 272 | 273 | private function createDeactivateForm() 274 | { 275 | return $this->createFormBuilder() 276 | ->getForm() 277 | ; 278 | } 279 | 280 | /** 281 | * Makes a Job entity active. 282 | * 283 | */ 284 | public function activateAction($id) 285 | { 286 | $form = $this->createActivateForm($id); 287 | $request = $this->getRequest(); 288 | 289 | if ('POST' === $request->getMethod()) { 290 | $form->bindRequest($request); 291 | 292 | if ($form->isValid()) { 293 | $em = $this->getDoctrine()->getEntityManager(); 294 | $entity = $em->getRepository('NineThousandJobqueueBundle:Job')->find($id); 295 | 296 | if (!$entity) { 297 | throw $this->createNotFoundException('Unable to find Job entity.'); 298 | } 299 | 300 | $entity->setActive(1); 301 | $entity->setStatus(NULL); 302 | $em->persist($entity); 303 | $em->flush(); 304 | } 305 | } 306 | 307 | return $this->redirect($this->generateUrl('jobqueue_job_edit', array('id' => $id))); 308 | } 309 | 310 | private function createActivateForm() 311 | { 312 | return $this->createFormBuilder() 313 | ->getForm() 314 | ; 315 | } 316 | 317 | /** 318 | * Queues a job entity for a single retry. 319 | * 320 | */ 321 | public function retryAction($id) 322 | { 323 | $form = $this->createRetryForm($id); 324 | $request = $this->getRequest(); 325 | 326 | if ('POST' === $request->getMethod()) { 327 | $form->bindRequest($request); 328 | 329 | if ($form->isValid()) { 330 | $em = $this->getDoctrine()->getEntityManager(); 331 | $entity = $em->getRepository('NineThousandJobqueueBundle:Job')->find($id); 332 | 333 | if (!$entity) { 334 | throw $this->createNotFoundException('Unable to find Job entity.'); 335 | } 336 | $maxRetries = $entity->getMaxRetries(); 337 | $maxRetries = ($entity->getAttempts() >= $maxRetries) ? ($maxRetries+1) : $maxRetries; 338 | $entity->setActive(0); 339 | $entity->setAttempts(0); 340 | $entity->setRetry(1); 341 | $entity->setMaxRetries($maxRetries); 342 | $entity->setStatus('retry'); 343 | $em->persist($entity); 344 | $em->flush(); 345 | } 346 | } 347 | 348 | return $this->redirect($this->generateUrl('jobqueue_job_edit', array('id' => $id))); 349 | } 350 | 351 | private function createRetryForm() 352 | { 353 | return $this->createFormBuilder() 354 | ->getForm() 355 | ; 356 | } 357 | 358 | /** 359 | * Removes submitted errant form fields 360 | * @param Symfony\Component\Form\Form $form 361 | * @param Symfony\Component\HttpFoundation\Request $request 362 | * @param Array $collections 363 | */ 364 | public function sanitizeCollections(Form &$form, Request &$request, Array $collections) { 365 | $formName = $form->getName(); 366 | $submission = $request->request->get($formName); 367 | foreach ($collections as $collection => $fields) { 368 | $total = count($submission[$collection])+1; 369 | for ($i = 0; $i < $total; $i++) { 370 | foreach ($fields as $field) { 371 | if (empty($submission[$collection][$i][$field])) { 372 | unset($submission[$collection][$i]); 373 | break; 374 | } 375 | } 376 | } 377 | } 378 | $request->request->set($formName, $submission); 379 | } 380 | 381 | /** 382 | * Populates the inverse of bidirectional collection relationships 383 | * @param Obj $entity 384 | * @param Array $collections 385 | * @param Doctrine\ORM\EntityManager 386 | */ 387 | public function setCollectionInverse(EntityManager &$em, &$entity, Array $collections) { 388 | foreach ($collections as $name => $mappedBy) { 389 | $collection = $entity->{'get' . ucwords($name)}(); 390 | foreach ($collection as $item) { 391 | $item->{'set' . ucwords($mappedBy)}($entity); 392 | $em->persist($item); 393 | } 394 | } 395 | } 396 | 397 | public function getPagination($query, $options, $total, $param) { 398 | 399 | $pages = array(); 400 | $current = $query['page']; 401 | unset($query['page']); 402 | unset($query['offset']); 403 | unset($query['limit']); 404 | 405 | $query = ($query = http_build_query($query)) ? '&'.$query : ''; 406 | $pCount = floor($total / $options['limit']); 407 | if ($remainder = $total % $options['limit']) { 408 | $pCount++; 409 | } 410 | $start = (($n = $current-$options['pages_before']) > 1) ? $n : 1; 411 | $end = (($m = $current+$options['pages_after']) < $pCount) ? $m : $pCount; 412 | for ($i=$start;$i<=$end;$i++) { 413 | array_push($pages, $i); 414 | } 415 | 416 | return array( 417 | 'current' => $current, 418 | 'pages' => $pages, 419 | 'last' => $pCount, 420 | 'param' => $param, 421 | 'query' => $query, 422 | ); 423 | } 424 | } 425 | -------------------------------------------------------------------------------- /Controller/ScheduledJobController.php: -------------------------------------------------------------------------------- 1 | container->getParameter('jobqueue.ui.options'); 30 | $pageParam = 'jpage'; 31 | $query['limit'] = $uiOptions['pagination']['limit']; 32 | $query['page'] = $this->getRequest()->query->get($pageParam) ?: 1; 33 | $query['status'] = null; 34 | $query['scheduled'] = 1; 35 | $query['reverse'] = $this->getRequest()->query->get('reverse') ?: null; 36 | $query['offset'] = ($query['limit'] * $query['page']) - $query['limit']; 37 | 38 | $em = $this->getDoctrine()->getEntityManager(); 39 | 40 | $result = $em->getRepository('NineThousandJobqueueBundle:Job')->findAllByQuery($query); 41 | 42 | $pagination = $this->getPagination($query, $uiOptions['pagination'], $result['totalResults'], $pageParam); 43 | 44 | $forms = array(); 45 | foreach ($result['entities'] as $entity) { 46 | $id = $entity->getId(); 47 | $forms[$id]['deactivate_form'] = $this->createDeactivateForm()->createView(); 48 | } 49 | 50 | return $this->render('NineThousandJobqueueBundle:ScheduledJob:index.html.twig', array( 51 | 'entities' => $result['entities'], 52 | 'forms' => $forms, 53 | 'pagination' => $pagination, 54 | )); 55 | } 56 | 57 | /** 58 | * Finds and displays a Job entity. 59 | * 60 | */ 61 | public function showAction($id) 62 | { 63 | $em = $this->getDoctrine()->getEntityManager(); 64 | 65 | $entity = $em->getRepository('NineThousandJobqueueBundle:Job')->find($id); 66 | 67 | if (!$entity) { 68 | throw $this->createNotFoundException('Unable to find Job entity.'); 69 | } 70 | 71 | return $this->render('NineThousandJobqueueBundle:ScheduledJob:show.html.twig', array( 72 | 'entity' => $entity, 73 | )); 74 | } 75 | 76 | /** 77 | * Displays a form to create a new Job entity. 78 | * 79 | */ 80 | public function newAction() 81 | { 82 | $entity = new Job(); 83 | $entity->setParams(array(new Param())); 84 | $entity->setArgs(array(new Arg())); 85 | $entity->setTags(array(new Tag())); 86 | $form = $this->createForm(new ScheduledJobType(), $entity, $this->container->getParameter('jobqueue.adapter.options')); 87 | 88 | return $this->render('NineThousandJobqueueBundle:ScheduledJob:new.html.twig', array( 89 | 'entity' => $entity, 90 | 'form' => $form->createView(), 91 | 'action' => $this->generateUrl('jobqueue_scheduledjob_create'), 92 | )); 93 | } 94 | 95 | /** 96 | * Creates a new Job entity. 97 | * 98 | */ 99 | public function createAction() 100 | { 101 | $entity = new Job(); 102 | $request = $this->getRequest(); 103 | $form = $this->createForm(new ScheduledJobType(), $entity, $this->container->getParameter('jobqueue.adapter.options')); 104 | 105 | if ('POST' === $request->getMethod()) { 106 | 107 | $this->sanitizeCollections($form, $request, array( 108 | 'params' => array('key','value'), 109 | 'args' => array('value'), 110 | 'tags' => array('value'), 111 | )); 112 | 113 | $form->bindRequest($request); 114 | 115 | if ($form->isValid()) { 116 | $em = $this->getDoctrine()->getEntityManager(); 117 | $this->setCollectionInverse($em, $entity, array( 118 | 'params' => 'job', 119 | 'args' => 'job', 120 | )); 121 | $entity->setCreateDate(new \DateTime); 122 | $entity->setActive(0); 123 | $em->persist($entity); 124 | $em->flush(); 125 | 126 | return $this->redirect($this->generateUrl('jobqueue_scheduledjob', array( 127 | 'id' => $entity->getId(), 128 | ))); 129 | 130 | } 131 | } 132 | 133 | return $this->render('NineThousandJobqueueBundle:ScheduledJob:new.html.twig', array( 134 | 'entity' => $entity, 135 | 'form' => $form->createView(), 136 | 'action' => $this->generateUrl('jobqueue_scheduledjob_create'), 137 | )); 138 | } 139 | 140 | /** 141 | * Displays a form to edit an existing Job entity. 142 | * 143 | */ 144 | public function editAction($id) 145 | { 146 | $em = $this->getDoctrine()->getEntityManager(); 147 | 148 | $entity = $em->getRepository('NineThousandJobqueueBundle:Job')->find($id); 149 | 150 | if (!$entity) { 151 | throw $this->createNotFoundException('Unable to find Job entity.'); 152 | } 153 | 154 | if (count($entity->getParams()) < 1) { 155 | $entity->setParams(array(new Param())); 156 | } 157 | 158 | if (count($entity->getArgs()) < 1) { 159 | $entity->setArgs(array(new Arg())); 160 | } 161 | 162 | if (count($entity->getTags()) < 1) { 163 | $entity->setTags(array(new Tag())); 164 | } 165 | 166 | $form = $this->createForm(new ScheduledJobType(), $entity, $this->container->getParameter('jobqueue.adapter.options')); 167 | 168 | $deactivateForm = $this->createDeactivateForm(); 169 | 170 | return $this->render('NineThousandJobqueueBundle:ScheduledJob:new.html.twig', array( 171 | 'entity' => $entity, 172 | 'deactivate_form' => $deactivateForm->createView(), 173 | 'form' => $form->createView(), 174 | 'action' => $this->generateUrl('jobqueue_scheduledjob_update', array('id' => $id)), 175 | )); 176 | } 177 | 178 | /** 179 | * Edits an existing Job entity. 180 | * 181 | */ 182 | public function updateAction($id) 183 | { 184 | $em = $this->getDoctrine()->getEntityManager(); 185 | 186 | $entity = $em->getRepository('NineThousandJobqueueBundle:Job')->find($id); 187 | 188 | if (!$entity) { 189 | throw $this->createNotFoundException('Unable to find Job entity.'); 190 | } 191 | 192 | $form = $this->createForm(new ScheduledJobType(), $entity, $this->container->getParameter('jobqueue.adapter.options')); 193 | 194 | $deactivateForm = $this->createDeactivateForm(); 195 | 196 | $request = $this->getRequest(); 197 | 198 | if ('POST' === $request->getMethod()) { 199 | $this->sanitizeCollections($form, $request, array( 200 | 'params' => array('key','value'), 201 | 'args' => array('value'), 202 | 'tags' => array('value'), 203 | )); 204 | 205 | $form->bindRequest($request); 206 | 207 | if ($form->isValid()) { 208 | $em = $this->getDoctrine()->getEntityManager(); 209 | $this->setCollectionInverse($em, $entity, array( 210 | 'params' => 'job', 211 | 'args' => 'job', 212 | )); 213 | $em->persist($entity); 214 | $em->flush(); 215 | 216 | return $this->redirect($this->generateUrl('jobqueue_scheduledjob_edit', array('id' => $id))); 217 | } 218 | } 219 | 220 | return $this->render('NineThousandJobqueueBundle:ScheduledJob:new.html.twig', array( 221 | 'entity' => $entity, 222 | 'deactivate_form' => $deactivateForm->createView(), 223 | 'form' => $form->createView(), 224 | 'action' => $this->generateUrl('jobqueue_scheduledjob_update', array('id' => $id)), 225 | )); 226 | } 227 | 228 | /** 229 | * Makes a Job entity inactive. 230 | * 231 | */ 232 | public function deactivateAction($id) 233 | { 234 | $form = $this->createDeactivateForm($id); 235 | $request = $this->getRequest(); 236 | 237 | if ('POST' === $request->getMethod()) { 238 | $form->bindRequest($request); 239 | 240 | if ($form->isValid()) { 241 | $em = $this->getDoctrine()->getEntityManager(); 242 | $entity = $em->getRepository('NineThousandJobqueueBundle:Job')->find($id); 243 | 244 | if (!$entity) { 245 | throw $this->createNotFoundException('Unable to find Job entity.'); 246 | } 247 | 248 | $entity->setSchedule(NULL); 249 | $em->persist($entity); 250 | $em->flush(); 251 | } 252 | } 253 | 254 | return $this->redirect($this->generateUrl('jobqueue_scheduledjob')); 255 | } 256 | 257 | private function createDeactivateForm() 258 | { 259 | return $this->createFormBuilder() 260 | ->getForm() 261 | ; 262 | } 263 | 264 | /** 265 | * Removes submitted errant form fields 266 | * @param Symfony\Component\Form\Form $form 267 | * @param Symfony\Component\HttpFoundation\Request $request 268 | * @param Array $collections 269 | */ 270 | public function sanitizeCollections(Form &$form, Request &$request, Array $collections) { 271 | $formName = $form->getName(); 272 | $submission = $request->request->get($formName); 273 | foreach ($collections as $collection => $fields) { 274 | $total = count($submission[$collection])+1; 275 | for ($i = 0; $i < $total; $i++) { 276 | foreach ($fields as $field) { 277 | if (empty($submission[$collection][$i][$field])) { 278 | unset($submission[$collection][$i]); 279 | break; 280 | } 281 | } 282 | } 283 | } 284 | $request->request->set($formName, $submission); 285 | } 286 | 287 | /** 288 | * Populates the inverse of bidirectional collection relationships 289 | * @param Obj $entity 290 | * @param Array $collections 291 | * @param Doctrine\ORM\EntityManager 292 | */ 293 | public function setCollectionInverse(EntityManager &$em, &$entity, Array $collections) { 294 | foreach ($collections as $name => $mappedBy) { 295 | $collection = $entity->{'get' . ucwords($name)}(); 296 | foreach ($collection as $item) { 297 | $item->{'set' . ucwords($mappedBy)}($entity); 298 | $em->persist($item); 299 | } 300 | } 301 | } 302 | 303 | public function getPagination($query, $options, $total, $param) { 304 | 305 | $pages = array(); 306 | $current = $query['page']; 307 | unset($query['page']); 308 | unset($query['offset']); 309 | unset($query['limit']); 310 | unset($query['scheduled']); 311 | $query = ($query = http_build_query($query)) ? '&'.$query : ''; 312 | $pCount = floor($total / $options['limit']); 313 | if ($remainder = $total % $options['limit']) { 314 | $pCount++; 315 | } 316 | $start = (($n = $current-$options['pages_before']) > 1) ? $n : 1; 317 | $end = (($m = $current+$options['pages_after']) < $pCount) ? $m : $pCount; 318 | for ($i=$start;$i<=$end;$i++) { 319 | array_push($pages, $i); 320 | } 321 | 322 | return array( 323 | 'current' => $current, 324 | 'pages' => $pages, 325 | 'last' => $pCount, 326 | 'param' => $param, 327 | 'query' => $query, 328 | ); 329 | } 330 | } 331 | -------------------------------------------------------------------------------- /DependencyInjection/Configuration.php: -------------------------------------------------------------------------------- 1 | root('nine_thousand_jobqueue', 'array'); 14 | 15 | $rootNode 16 | ->children() 17 | ->arrayNode('control') 18 | ->addDefaultsIfNotSet() 19 | ->children() 20 | ->scalarNode('class')->defaultValue('NineThousand\\Jobqueue\\Service\\JobqueueControl')->end() 21 | ->end() 22 | ->end() 23 | ->arrayNode('job') 24 | ->addDefaultsIfNotSet() 25 | ->children() 26 | ->scalarNode('class')->defaultValue('NineThousand\\Jobqueue\\Job\\StandardJob')->end() 27 | ->end() 28 | ->end() 29 | ->arrayNode('adapter') 30 | ->addDefaultsIfNotSet() 31 | ->children() 32 | ->scalarNode('class')->defaultValue('NineThousand\\Jobqueue\\Vendor\\Doctrine\\Adapter\\Queue\\DoctrineQueueAdapter')->end() 33 | ->arrayNode('options') 34 | ->addDefaultsIfNotSet() 35 | ->children() 36 | ->scalarNode('cron_class')->defaultValue('NineThousand\\Jobqueue\\Util\\Cron\\CronExpression')->end() 37 | ->scalarNode('job_entity_class')->defaultValue('NineThousand\\Bundle\\NineThousandJobqueueBundle\\Entity\\Job')->end() 38 | ->scalarNode('job_adapter_class')->defaultValue('NineThousand\\Bundle\\NineThousandJobqueueBundle\\Vendor\\Doctrine\\Adapter\\Job\\Symfony2DoctrineJobAdapter')->end() 39 | ->scalarNode('history_entity_class')->defaultValue('NineThousand\\Bundle\\NineThousandJobqueueBundle\\Entity\\History')->end() 40 | ->scalarNode('history_adapter_class')->defaultValue('NineThousand\\Jobqueue\\Vendor\\Doctrine\\Adapter\\History\\DoctrineHistoryAdapter')->end() 41 | ->scalarNode('log_adapter_class')->defaultValue('NineThousand\\Jobqueue\\Vendor\\Doctrine\\Adapter\\Log\\MonologAdapter')->end() 42 | ->arrayNode('jobcontrol') 43 | ->addDefaultsIfNotSet() 44 | ->children() 45 | ->arrayNode('type_mapping') 46 | ->addDefaultsIfNotSet() 47 | ->children() 48 | ->scalarNode('SymfonyConsoleJobControl')->defaultValue('NineThousand\\Jobqueue\\Vendor\\Symfony2\\Adapter\\Job\\Control\\Symfony2ConsoleJobControl')->end() 49 | ->end() 50 | ->end() 51 | ->end() 52 | ->end() 53 | ->end() 54 | ->end() 55 | ->end() 56 | ->end() 57 | ->arrayNode('ui') 58 | ->addDefaultsIfNotSet() 59 | ->children() 60 | ->arrayNode('pagination') 61 | ->addDefaultsIfNotSet() 62 | ->children() 63 | ->scalarNode('limit')->defaultValue('40')->end() 64 | ->scalarNode('pages_before')->defaultValue('5')->end() 65 | ->scalarNode('pages_after')->defaultValue('5')->end() 66 | ->end() 67 | ->end() 68 | ->end() 69 | ->end() 70 | ->end() 71 | ; 72 | return $treeBuilder; 73 | } 74 | } 75 | 76 | -------------------------------------------------------------------------------- /DependencyInjection/NineThousandJobqueueExtension.php: -------------------------------------------------------------------------------- 1 | load('jobqueue.xml'); 17 | 18 | $configuration = new Configuration(); 19 | $config = $this->processConfiguration($configuration, $configs); 20 | 21 | $container->setParameter('jobqueue.job.class', $config['job']['class']); 22 | $container->setParameter('jobqueue.control.class', $config['control']['class']); 23 | $container->setParameter('jobqueue.adapter.class', $config['adapter']['class']); 24 | $container->setParameter('jobqueue.adapter.options', $config['adapter']['options']); 25 | $container->setParameter('jobqueue.ui.options', $config['ui']); 26 | } 27 | 28 | /** 29 | * Returns the base path for the XSD files. 30 | * 31 | * @return string The XSD base path 32 | */ 33 | public function getXsdValidationBasePath() 34 | { 35 | return __DIR__.'/../Resources/config/schema'; 36 | } 37 | 38 | public function getNamespace() 39 | { 40 | return 'http://www.ninethousand.org/schema/dic/jobqueue'; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /Entity/Arg.php: -------------------------------------------------------------------------------- 1 | 13 | * @copyright 2011 NineThousand (https://github.com/organizations/NineThousand) 14 | * @license http://www.opensource.org/licenses/bsd-license.php New BSD Licence 15 | * @link https://github.com/NineThousand/ninethousand-jobqueue 16 | */ 17 | 18 | use Doctrine\ORM\Mapping as ORM; 19 | 20 | /** 21 | * Arg 22 | * 23 | * @ORM\Table(name="jobqueue_arg") 24 | * @ORM\Entity 25 | */ 26 | class Arg 27 | { 28 | 29 | /** 30 | * @ORM\Id 31 | * @ORM\Column(type="integer") 32 | * @ORM\GeneratedValue(strategy="AUTO") 33 | */ 34 | protected $id; 35 | 36 | /** 37 | * @return int 38 | */ 39 | public function getId() 40 | { 41 | return $this->id; 42 | } 43 | 44 | /** 45 | * @param int $id 46 | */ 47 | public function setId($id) 48 | { 49 | $this->id = $id; 50 | } 51 | 52 | 53 | /** 54 | * @ORM\ManyToOne(targetEntity="Job", inversedBy="args") 55 | */ 56 | protected $job; 57 | 58 | /** 59 | * @return int 60 | */ 61 | public function getJob() 62 | { 63 | return $this->job; 64 | } 65 | 66 | /** 67 | * @param int $job 68 | */ 69 | public function setJob($job) 70 | { 71 | $this->job = $job; 72 | } 73 | 74 | 75 | 76 | /** 77 | * @ORM\Column(type="string") 78 | */ 79 | protected $value; 80 | 81 | /** 82 | * @return string 83 | */ 84 | public function getValue() 85 | { 86 | return $this->value; 87 | } 88 | 89 | /** 90 | * @param string $value 91 | */ 92 | public function setValue($value) 93 | { 94 | $this->value = $value; 95 | } 96 | 97 | 98 | 99 | /** 100 | * @ORM\Column(type="integer") 101 | */ 102 | protected $active = 1; 103 | 104 | /** 105 | * @return int 106 | */ 107 | public function getActive() 108 | { 109 | return $this->active; 110 | } 111 | 112 | /** 113 | * @param int $active 114 | */ 115 | public function setActive($active) 116 | { 117 | $this->active = $active; 118 | } 119 | 120 | 121 | } 122 | -------------------------------------------------------------------------------- /Entity/History.php: -------------------------------------------------------------------------------- 1 | 13 | * @copyright 2011 NineThousand (https://github.com/organizations/NineThousand) 14 | * @license http://www.opensource.org/licenses/bsd-license.php New BSD Licence 15 | * @link https://github.com/NineThousand/ninethousand-jobqueue 16 | */ 17 | 18 | use Doctrine\ORM\Mapping as ORM; 19 | 20 | /** 21 | * History 22 | * 23 | * @ORM\Table(name="jobqueue_history") 24 | * @ORM\Entity 25 | */ 26 | class History 27 | { 28 | 29 | /** 30 | * @ORM\Id 31 | * @ORM\Column(type="integer") 32 | * @ORM\GeneratedValue(strategy="AUTO") 33 | */ 34 | protected $id; 35 | 36 | /* 37 | * @return int 38 | */ 39 | public function getId() 40 | { 41 | return $this->id; 42 | } 43 | 44 | /* 45 | * @param int $id 46 | */ 47 | public function setId($id) 48 | { 49 | $this->id = $id; 50 | } 51 | 52 | 53 | /** 54 | * @ORM\ManyToOne(targetEntity="Job") 55 | * @ORM\JoinColumn(name="job", referencedColumnName="id") 56 | */ 57 | protected $job; 58 | 59 | /** 60 | * @return int 61 | */ 62 | public function getJob() 63 | { 64 | return $this->job; 65 | } 66 | 67 | /** 68 | * @param int $job 69 | */ 70 | public function setJob($job) 71 | { 72 | $this->job = $job; 73 | } 74 | 75 | 76 | 77 | /** 78 | * @ORM\Column(nullable="true", type="datetime") 79 | */ 80 | protected $timestamp; 81 | 82 | /** 83 | * @return \DateTime 84 | */ 85 | public function getTimestamp() 86 | { 87 | return $this->timestamp; 88 | } 89 | 90 | /** 91 | * @param \DateTime $timestamp 92 | */ 93 | public function setTimestamp(\DateTime $timestamp) 94 | { 95 | $this->timestamp = $timestamp; 96 | } 97 | 98 | 99 | /** 100 | * @ORM\Column(nullable="true", type="string") 101 | */ 102 | protected $status; 103 | 104 | /** 105 | * @return string 106 | */ 107 | public function getStatus() 108 | { 109 | return $this->status; 110 | } 111 | 112 | /** 113 | * @param string $status 114 | */ 115 | public function setStatus($status) 116 | { 117 | $this->status = $status; 118 | } 119 | 120 | 121 | /** 122 | * @ORM\Column(nullable="true", type="text", nullable="true") 123 | */ 124 | protected $message; 125 | 126 | /** 127 | * @return string 128 | */ 129 | public function getMessage() 130 | { 131 | return $this->message; 132 | } 133 | 134 | /** 135 | * @param string $message 136 | */ 137 | public function setMessage($message) 138 | { 139 | $this->message = $message; 140 | } 141 | 142 | 143 | /** 144 | * @ORM\Column(nullable="true", type="string") 145 | */ 146 | protected $severity; 147 | 148 | /** 149 | * @return string 150 | */ 151 | public function getSeverity() 152 | { 153 | return $this->severity; 154 | } 155 | 156 | /** 157 | * @param string $severity 158 | */ 159 | public function setSeverity($severity) 160 | { 161 | $this->severity = $severity; 162 | } 163 | 164 | 165 | /** 166 | * @ORM\Column(type="integer") 167 | */ 168 | protected $active; 169 | 170 | /** 171 | * @return int 172 | */ 173 | public function getActive() 174 | { 175 | return $this->active; 176 | } 177 | 178 | /** 179 | * @param int $active 180 | */ 181 | public function setActive($active) 182 | { 183 | $this->active = $active; 184 | } 185 | 186 | 187 | } 188 | -------------------------------------------------------------------------------- /Entity/Job.php: -------------------------------------------------------------------------------- 1 | 13 | * @copyright 2011 NineThousand (https://github.com/organizations/NineThousand) 14 | * @license http://www.opensource.org/licenses/bsd-license.php New BSD Licence 15 | * @link https://github.com/NineThousand/ninethousand-jobqueue 16 | */ 17 | 18 | use Doctrine\ORM\Mapping as ORM; 19 | 20 | use NineThousand\Bundle\NineThousandJobqueueBundle\Entity\Param; 21 | use NineThousand\Bundle\NineThousandJobqueueBundle\Entity\Arg; 22 | use Doctrine\Common\Collections\ArrayCollection; 23 | 24 | /** 25 | * Job 26 | * 27 | * @ORM\Table(name="jobqueue_job") 28 | * @ORM\Entity(repositoryClass="NineThousand\Bundle\NineThousandJobqueueBundle\Repository\JobRepository") 29 | */ 30 | class Job 31 | { 32 | 33 | public function __construct() 34 | { 35 | $this->args = new ArrayCollection(); 36 | $this->params = new ArrayCollection(); 37 | $this->tags = new ArrayCollection(); 38 | $this->history = new ArrayCollection(); 39 | } 40 | 41 | 42 | /** 43 | * @ORM\Id 44 | * @ORM\Column(type="integer") 45 | * @ORM\GeneratedValue(strategy="AUTO") 46 | */ 47 | protected $id; 48 | 49 | /** 50 | * @return int 51 | */ 52 | public function getId() 53 | { 54 | return $this->id; 55 | } 56 | 57 | /** 58 | * @param int $id 59 | */ 60 | public function setId($id) 61 | { 62 | $this->id = $id; 63 | } 64 | 65 | /** 66 | * @ORM\Column(nullable="true", type="string") 67 | */ 68 | protected $name; 69 | 70 | /** 71 | * @return string 72 | */ 73 | public function getName() 74 | { 75 | return $this->name; 76 | } 77 | 78 | /** 79 | * @param string $name 80 | */ 81 | public function setName($name) 82 | { 83 | $this->name = $name; 84 | } 85 | 86 | 87 | /** 88 | * @ORM\Column(type="integer") 89 | */ 90 | protected $retry = 0; 91 | 92 | /** 93 | * @return int 94 | */ 95 | public function getRetry() 96 | { 97 | return $this->retry; 98 | } 99 | 100 | /** 101 | * @param int $retry 102 | */ 103 | public function setRetry($retry) 104 | { 105 | $this->retry = $retry; 106 | } 107 | 108 | /** 109 | * @ORM\Column(type="integer") 110 | */ 111 | protected $cooldown = 0; 112 | 113 | /** 114 | * @return int 115 | */ 116 | public function getCooldown() 117 | { 118 | return $this->cooldown; 119 | } 120 | 121 | /** 122 | * @param int $cooldown 123 | */ 124 | public function setCooldown($cooldown) 125 | { 126 | $this->cooldown = $cooldown; 127 | } 128 | 129 | 130 | /** 131 | * @ORM\Column(name="max_retries", type="integer") 132 | */ 133 | protected $maxRetries = 0; 134 | 135 | /** 136 | * @return int 137 | */ 138 | public function getMaxRetries() 139 | { 140 | return $this->maxRetries; 141 | } 142 | 143 | /** 144 | * @param int $maxRetries 145 | */ 146 | public function setMaxRetries($maxRetries) 147 | { 148 | $this->maxRetries = $maxRetries; 149 | } 150 | 151 | 152 | /** 153 | * @ORM\Column(type="integer") 154 | */ 155 | protected $attempts = 0; 156 | 157 | /** 158 | * @return int 159 | */ 160 | public function getAttempts() 161 | { 162 | return $this->attempts; 163 | } 164 | 165 | /** 166 | * @param int $attempts 167 | */ 168 | public function setAttempts($attempts) 169 | { 170 | $this->attempts = $attempts; 171 | } 172 | 173 | 174 | /** 175 | * @ORM\Column(nullable="true", type="text") 176 | */ 177 | protected $executable; 178 | 179 | /** 180 | * @return string 181 | */ 182 | public function getExecutable() 183 | { 184 | return $this->executable; 185 | } 186 | 187 | /** 188 | * @param string $executable 189 | */ 190 | public function setExecutable($executable) 191 | { 192 | $this->executable = $executable; 193 | } 194 | 195 | 196 | /** 197 | * @ORM\Column(nullable="true", type="string") 198 | */ 199 | protected $type; 200 | 201 | /** 202 | * @return string 203 | */ 204 | public function getType() 205 | { 206 | return $this->type; 207 | } 208 | 209 | /** 210 | * @param string $type 211 | */ 212 | public function setType($type) 213 | { 214 | $this->type = $type; 215 | } 216 | 217 | 218 | /** 219 | * @ORM\Column(nullable="true", type="string") 220 | */ 221 | protected $status; 222 | 223 | /** 224 | * @return string 225 | */ 226 | public function getStatus() 227 | { 228 | return $this->status; 229 | } 230 | 231 | /** 232 | * @param string $status 233 | */ 234 | public function setStatus($status) 235 | { 236 | $this->status = $status; 237 | } 238 | 239 | 240 | /** 241 | * @ORM\Column(name="create_date", type="datetime") 242 | */ 243 | protected $createDate; 244 | 245 | /** 246 | * @return \DateTime 247 | */ 248 | public function getCreateDate() 249 | { 250 | return $this->createDate; 251 | } 252 | 253 | /** 254 | * @param \DateTime $date 255 | */ 256 | public function setCreateDate(\DateTime $date) 257 | { 258 | $this->createDate = $date; 259 | } 260 | 261 | 262 | /** 263 | * @ORM\Column(name="last_run", nullable="true", type="datetime") 264 | */ 265 | protected $lastrunDate; 266 | 267 | /** 268 | * @return \DateTime 269 | */ 270 | public function getLastrunDate() 271 | { 272 | return $this->lastrunDate; 273 | } 274 | 275 | /** 276 | * @param NULL | \DateTime $date 277 | */ 278 | public function setLastrunDate($date) 279 | { 280 | $this->lastrunDate = $date; 281 | } 282 | 283 | 284 | /** 285 | * @ORM\Column(type="integer") 286 | */ 287 | protected $active = 0; 288 | 289 | /** 290 | * @return int 291 | */ 292 | public function getActive() 293 | { 294 | return $this->active; 295 | } 296 | 297 | /** 298 | * @param int $active 299 | */ 300 | public function setActive($active) 301 | { 302 | $this->active = $active; 303 | } 304 | 305 | 306 | /** 307 | * @ORM\Column(nullable="true", type="string") 308 | */ 309 | protected $schedule; 310 | 311 | /** 312 | * @return string 313 | */ 314 | public function getSchedule() 315 | { 316 | return $this->schedule; 317 | } 318 | 319 | /** 320 | * @param string $schedule 321 | */ 322 | public function setSchedule($schedule) 323 | { 324 | $this->schedule = $schedule; 325 | } 326 | 327 | /** 328 | * @ORM\Column(type="integer") 329 | */ 330 | protected $parent = 0; 331 | 332 | /** 333 | * @return int 334 | */ 335 | public function getParent() 336 | { 337 | return $this->parent; 338 | } 339 | 340 | /** 341 | * @param int $parent 342 | */ 343 | public function setParent($parent) 344 | { 345 | $this->parent = $parent; 346 | } 347 | 348 | 349 | /** 350 | * @ORM\OneToMany(targetEntity="Param", mappedBy="job", cascade={"persist", "remove"}, orphanRemoval=true) 351 | */ 352 | protected $params; 353 | 354 | /** 355 | * @return Doctrine\Common\Collections\ArrayCollection 356 | */ 357 | public function getParams() 358 | { 359 | return $this->params; 360 | } 361 | 362 | /** 363 | * @param Doctrine\Common\Collections\ArrayCollection $params 364 | */ 365 | public function setParams($params) 366 | { 367 | $this->params = $params; 368 | } 369 | 370 | public function addParam(Param $param) { 371 | $this->params[] = $param; 372 | $param->setJob($this); 373 | } 374 | 375 | 376 | /** 377 | * @ORM\OneToMany(targetEntity="Arg", mappedBy="job", cascade={"persist", "remove"}, orphanRemoval=true) 378 | */ 379 | protected $args; 380 | 381 | /** 382 | * @return Doctrine\Common\Collections\ArrayCollection 383 | */ 384 | public function getArgs() 385 | { 386 | return $this->args; 387 | } 388 | 389 | /** 390 | * @param Doctrine\Common\Collections\ArrayCollection $args 391 | */ 392 | public function setArgs($args) 393 | { 394 | $this->args = $args; 395 | } 396 | 397 | public function addArg(Arg $arg) { 398 | $this->args[] = $arg; 399 | $arg->setJob($this); 400 | } 401 | 402 | /** 403 | * @ORM\ManyToMany(targetEntity="Tag", cascade={"persist", "remove"}) 404 | * @ORM\JoinTable(name="jobqueue_job_tag", 405 | * joinColumns={@ORM\JoinColumn(name="job_id", referencedColumnName="id")}, 406 | * inverseJoinColumns={@ORM\JoinColumn(name="tag_id", referencedColumnName="id", unique=true)} 407 | * ) 408 | */ 409 | protected $tags; 410 | 411 | /** 412 | * @return Doctrine\Common\Collections\ArrayCollection 413 | */ 414 | public function getTags() 415 | { 416 | return $this->tags; 417 | } 418 | 419 | /** 420 | * @param Doctrine\Common\Collections\ArrayCollection $tags 421 | */ 422 | public function setTags($tags) 423 | { 424 | $this->tags = $tags; 425 | } 426 | 427 | /** 428 | * @ORM\OneToMany(targetEntity="History", mappedBy="job") 429 | */ 430 | protected $history; 431 | 432 | /** 433 | * @return Doctrine\Common\Collections\ArrayCollection 434 | */ 435 | public function getHistory() 436 | { 437 | return $this->history; 438 | } 439 | 440 | /** 441 | * @param Doctrine\Common\Collections\ArrayCollection $history 442 | */ 443 | public function setHistory($history) 444 | { 445 | $this->history = $history; 446 | } 447 | 448 | } 449 | -------------------------------------------------------------------------------- /Entity/Param.php: -------------------------------------------------------------------------------- 1 | 13 | * @copyright 2011 NineThousand (https://github.com/organizations/NineThousand) 14 | * @license http://www.opensource.org/licenses/bsd-license.php New BSD Licence 15 | * @link https://github.com/NineThousand/ninethousand-jobqueue 16 | */ 17 | 18 | use Doctrine\ORM\Mapping as ORM; 19 | 20 | /** 21 | * Param 22 | * 23 | * @ORM\Table(name="jobqueue_param") 24 | * @ORM\Entity 25 | */ 26 | class Param 27 | { 28 | 29 | /** 30 | * @ORM\Id 31 | * @ORM\Column(type="integer") 32 | * @ORM\GeneratedValue(strategy="AUTO") 33 | */ 34 | protected $id; 35 | 36 | /** 37 | * @return int 38 | */ 39 | public function getId() 40 | { 41 | return $this->id; 42 | } 43 | 44 | /** 45 | * @param int $id 46 | */ 47 | public function setId($id) 48 | { 49 | $this->id = $id; 50 | } 51 | 52 | 53 | /** 54 | * @ORM\ManyToOne(targetEntity="Job", inversedBy="params") 55 | */ 56 | protected $job; 57 | 58 | /** 59 | * @return int 60 | */ 61 | public function getJob() 62 | { 63 | return $this->job; 64 | } 65 | 66 | /** 67 | * @param int $job 68 | */ 69 | public function setJob($job) 70 | { 71 | $this->job = $job; 72 | } 73 | 74 | 75 | 76 | /** 77 | * @ORM\Column(name="param_name", type="string") 78 | */ 79 | protected $key; 80 | 81 | /** 82 | * @return string 83 | */ 84 | public function getKey() 85 | { 86 | return $this->key; 87 | } 88 | 89 | /** 90 | * @param string $key 91 | */ 92 | public function setKey($key) 93 | { 94 | $this->key = $key; 95 | } 96 | 97 | 98 | /** 99 | * @ORM\Column(type="string") 100 | */ 101 | protected $value; 102 | 103 | /** 104 | * @return string 105 | */ 106 | public function getValue() 107 | { 108 | return $this->value; 109 | } 110 | 111 | /** 112 | * @param string $value 113 | */ 114 | public function setValue($value) 115 | { 116 | $this->value = $value; 117 | } 118 | 119 | 120 | 121 | /** 122 | * @ORM\Column(type="integer") 123 | */ 124 | protected $active = 1; 125 | 126 | /** 127 | * @return int 128 | */ 129 | public function getActive() 130 | { 131 | return $this->active; 132 | } 133 | 134 | /** 135 | * @param int $active 136 | */ 137 | public function setActive($active) 138 | { 139 | $this->active = $active; 140 | } 141 | 142 | 143 | } 144 | -------------------------------------------------------------------------------- /Entity/Tag.php: -------------------------------------------------------------------------------- 1 | 13 | * @copyright 2011 NineThousand (https://github.com/organizations/NineThousand) 14 | * @license http://www.opensource.org/licenses/bsd-license.php New BSD Licence 15 | * @link https://github.com/NineThousand/ninethousand-jobqueue 16 | */ 17 | 18 | use Doctrine\ORM\Mapping as ORM; 19 | 20 | /** 21 | * Tag 22 | * 23 | * @ORM\Table(name="jobqueue_tag") 24 | * @ORM\Entity 25 | */ 26 | class Tag 27 | { 28 | 29 | /** 30 | * @ORM\Id 31 | * @ORM\Column(type="integer") 32 | * @ORM\GeneratedValue(strategy="AUTO") 33 | */ 34 | protected $id; 35 | 36 | /** 37 | * @return int 38 | */ 39 | public function getId() 40 | { 41 | return $this->id; 42 | } 43 | 44 | /** 45 | * @param int $id 46 | */ 47 | public function setId($id) 48 | { 49 | $this->id = $id; 50 | } 51 | 52 | 53 | /** 54 | * @ORM\ManyToMany(targetEntity="Job", mappedBy="tags") 55 | */ 56 | protected $job; 57 | 58 | /** 59 | * @return int 60 | */ 61 | public function getJob() 62 | { 63 | return $this->job; 64 | } 65 | 66 | /** 67 | * @param int $job 68 | */ 69 | public function setJob($job) 70 | { 71 | $this->job = $job; 72 | } 73 | 74 | 75 | 76 | /** 77 | * @ORM\Column(type="string") 78 | */ 79 | protected $value; 80 | 81 | /** 82 | * @return string 83 | */ 84 | public function getValue() 85 | { 86 | return $this->value; 87 | } 88 | 89 | /** 90 | * @param string $value 91 | */ 92 | public function setValue($value) 93 | { 94 | $this->value = $value; 95 | } 96 | 97 | 98 | 99 | /** 100 | * @ORM\Column(type="integer") 101 | */ 102 | protected $active = 1; 103 | 104 | /** 105 | * @return int 106 | */ 107 | public function getActive() 108 | { 109 | return $this->active; 110 | } 111 | 112 | /** 113 | * @param int $active 114 | */ 115 | public function setActive($active) 116 | { 117 | $this->active = $active; 118 | } 119 | 120 | 121 | } 122 | -------------------------------------------------------------------------------- /Form/ArgType.php: -------------------------------------------------------------------------------- 1 | add('value', 'text', array( 14 | 'label' => 'Arg', 15 | 'required' => false)) 16 | ; 17 | } 18 | 19 | public function getDefaultOptions(array $options) 20 | { 21 | return array( 22 | 'data_class' => 'NineThousand\Bundle\NineThousandJobqueueBundle\Entity\Arg', 23 | ); 24 | } 25 | 26 | public function getName() 27 | { 28 | return 'jobqueue_arg'; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Form/JobType.php: -------------------------------------------------------------------------------- 1 | add('retry', 'choice', array( 18 | 'choices' => array('1' => 'Yes', '0' => 'No'), 19 | 'expanded' => true, 20 | 'required' => true, 21 | )) 22 | ->add('cooldown', 'integer', array( 23 | 'label' => 'Retry Cooldown (in seconds)', 24 | 'required' => false, 25 | )) 26 | ->add('maxRetries', 'integer', array( 27 | 'label' => 'Maximum Retries', 28 | 'required' => false, 29 | )) 30 | ->add('executable', 'text', array( 31 | 'label' => 'Executable (Full Path)', 32 | 'required' => true, 33 | )) 34 | ->add('_token', 'csrf') 35 | ->add('params', 'collection', array( 36 | 'type' => new ParamType(), 37 | 'allow_add' => true, 38 | 'allow_delete' => true, 39 | )) 40 | ->add('args', 'collection', array( 41 | 'type' => new ArgType(), 42 | 'allow_add' => true, 43 | 'allow_delete' => true, 44 | )) 45 | ->add('tags', 'collection', array( 46 | 'type' => new TagType(), 47 | 'allow_add' => true, 48 | 'allow_delete' => true, 49 | )) 50 | ; 51 | 52 | if (isset($options['jobcontrol']['type_mapping']) && !empty($options['jobcontrol']['type_mapping'])) { 53 | $list = array(); 54 | foreach ($options['jobcontrol']['type_mapping'] as $key => $val) { 55 | $list[$key] = $key; 56 | } 57 | $builder->add('type', 'choice', array( 58 | 'choices' => $list, 59 | 'required' => true, 60 | )); 61 | } 62 | } 63 | 64 | public function getDefaultOptions(array $options) 65 | { 66 | return array_merge($options, array( 67 | 'data_class' => 'NineThousand\Bundle\NineThousandJobqueueBundle\Entity\Job', 68 | 'csrf_protection' => true, 69 | 'csrf_field_name' => '_token', 70 | 'intention' => 'jobqueue_job_nekot', 71 | )); 72 | } 73 | 74 | public function getName() 75 | { 76 | return 'jobqueue_job'; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Form/ParamType.php: -------------------------------------------------------------------------------- 1 | add('key', 'text', array( 14 | 'label' => 'Parameter Key', 15 | 'required' => false)) 16 | ->add('value', 'text', array( 17 | 'label' => 'Parameter Value', 18 | 'required' => false)) 19 | ; 20 | } 21 | 22 | public function getDefaultOptions(array $options) 23 | { 24 | return array( 25 | 'data_class' => 'NineThousand\Bundle\NineThousandJobqueueBundle\Entity\Param', 26 | ); 27 | } 28 | 29 | public function getName() 30 | { 31 | return 'jobqueue_param'; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Form/ScheduledJobType.php: -------------------------------------------------------------------------------- 1 | add('name', 'text', array( 18 | 'label' => 'Task Name', 19 | 'required' => true, 20 | )) 21 | ->add('schedule', 'text', array( 22 | 'label' => 'Schedule (Cron notation)', 23 | 'required' => true, 24 | )) 25 | ->add('executable', 'text', array( 26 | 'label' => 'Executable (Full Path)', 27 | 'required' => true, 28 | )) 29 | ->add('_token', 'csrf') 30 | ->add('params', 'collection', array( 31 | 'type' => new ParamType(), 32 | 'allow_add' => true, 33 | 'allow_delete' => true, 34 | )) 35 | ->add('args', 'collection', array( 36 | 'type' => new ArgType(), 37 | 'allow_add' => true, 38 | 'allow_delete' => true, 39 | )) 40 | ->add('tags', 'collection', array( 41 | 'type' => new TagType(), 42 | 'allow_add' => true, 43 | 'allow_delete' => true, 44 | )) 45 | ; 46 | 47 | if (isset($options['jobcontrol']['type_mapping']) && !empty($options['jobcontrol']['type_mapping'])) { 48 | $list = array(); 49 | foreach ($options['jobcontrol']['type_mapping'] as $key => $val) { 50 | $list[$key] = $key; 51 | } 52 | $builder->add('type', 'choice', array( 53 | 'choices' => $list, 54 | 'required' => true, 55 | )); 56 | } 57 | } 58 | 59 | public function getDefaultOptions(array $options) 60 | { 61 | return array_merge($options, array( 62 | 'data_class' => 'NineThousand\Bundle\NineThousandJobqueueBundle\Entity\Job', 63 | 'csrf_protection' => true, 64 | 'csrf_field_name' => '_token', 65 | 'intention' => 'jobqueue_scheduledjob_nekot', 66 | )); 67 | } 68 | 69 | public function getName() 70 | { 71 | return 'jobqueue_scheduledjob'; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Form/TagType.php: -------------------------------------------------------------------------------- 1 | add('value', 'text', array( 14 | 'label' => 'Tag', 15 | 'required' => false)) 16 | ; 17 | } 18 | 19 | public function getDefaultOptions(array $options) 20 | { 21 | return array( 22 | 'data_class' => 'NineThousand\Bundle\NineThousandJobqueueBundle\Entity\Tag', 23 | ); 24 | } 25 | 26 | public function getName() 27 | { 28 | return 'jobqueue_tag'; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /NineThousandJobqueueBundle.php: -------------------------------------------------------------------------------- 1 | registerNamespaces(array( 43 | //... 44 | 'NineThousand' => array(__DIR__.'/../vendor/ninethousand-jobqueue/lib', __DIR__.'/../vendor/bundles'), 45 | // for mtdowlings cron library 46 | 'Cron' => array(__DIR__.'/../vendor/cron-expression/src'), 47 | )); 48 | 49 | ### appKernel.php ### 50 | Add The Jobqueue bundle to your kernel bootstrap sequence 51 | 52 | public function registerBundles() 53 | { 54 | $bundles = array( 55 | //... 56 | new NineThousand\Bundle\NineThousandJobqueueBundle\NineThousandJobqueueBundle(), 57 | ); 58 | //... 59 | 60 | return $bundles; 61 | } 62 | 63 | ### config.yml ### 64 | By Default, Jobqueue has a sensible configuration which will use Doctrine ORM and the default EM available in your project. If you need to change any configuration setting and/or extend the jobqueue library, you could do it by adding this configuration to your project config. Only the values that need to be changed should be added, the jobqueue extension will merge your config into its defaults. 65 | 66 | app/config.yml 67 | 68 | nine_thousand_jobqueue: 69 | job: 70 | class: NineThousand\Jobqueue\Job\StandardJob 71 | control: 72 | class: NineThousand\Jobqueue\Service\JobqueueControl 73 | adapter: 74 | class: NineThousand\Jobqueue\Vendor\Doctrine\Adapter\Queue\DoctrineQueueAdapter 75 | options: 76 | cron_class: NineThousand\Jobqueue\Util\Cron\CronExpression 77 | job_entity_class: NineThousand\Bundle\NineThousandJobqueueBundle\Entity\Job 78 | job_adapter_class: NineThousand\Bundle\NineThousandJobqueueBundle\Vendor\Doctrine\Adapter\Job\Symfony2DoctrineJobAdapter 79 | history_entity_class: NineThousand\Bundle\NineThousandJobqueueBundle\Entity\History 80 | history_adapter_class: NineThousand\Jobqueue\Vendor\Doctrine\Adapter\History\DoctrineHistoryAdapter 81 | log_adapter_class: NineThousand\Jobqueue\Vendor\Doctrine\Adapter\Log\MonologAdapter 82 | jobcontrol: 83 | type_mapping: 84 | SymfonyConsoleJobControl: NineThousand\Jobqueue\Vendor\Symfony2\Adapter\Job\Control\Symfony2ConsoleJobControl 85 | ui: 86 | pagination: 87 | limit: 40 88 | pages_before: 5 89 | pages_after: 5 90 | 91 | ### routing.yml ### 92 | Import the bundle routing into your projects routing config with your desired prefix: 93 | 94 | ninethousand_jobqueue: 95 | resource: "@NineThousandJobqueueBundle/Resources/config/routing.xml" 96 | prefix: /jobqueue 97 | 98 | ### initialize database ### 99 | #### doctrine ORM #### 100 | If you're using the doctrine configuration that's default to your project, it could be as simple as running a schema update from the command line: 101 | 102 | jesse@picard:~/ninethousand.org$ php app/console doctrine:schema:update 103 | 104 | #### other database adapters #### 105 | Since no other database adapters have been created yet, if you extend the data persistence layer, bear in mind that your tables or data structure will need to be initialized somehow. 106 | 107 | ## Usage ## 108 | 109 | ### Controller ### 110 | To access your queues from the controller Simply call the jobqueue.control service. 111 | 112 | namespace NineThousand\Bundle\NineThousandJobqueueBundle\Controller; 113 | 114 | use Symfony\Bundle\FrameworkBundle\Controller\Controller; 115 | 116 | class DefaultController extends Controller 117 | { 118 | public function indexAction() 119 | { 120 | $queueControl = $this->get('jobqueue.control'); 121 | 122 | return $this->render('JobqueueBundle:Default:index.html.twig', array( 123 | 'activeQueue' => $queueControl->getActiveQueue(), 124 | 'retryQueue' => $queueControl->getRetryQueue(), 125 | 'scheduleQueue' => $queueControl->getScheduleQueue() 126 | )); 127 | } 128 | } 129 | 130 | #### To create a new Job #### 131 | Here is a gist that shows the code api of how to create a new job 132 | https://gist.github.com/1154182 133 | 134 | ### View ### 135 | All Queues should implement PHPs \Iterator interface, so displaying the queue information in your template is simply a matter of iterating over them in a foreach loop: 136 | 137 | {% extends 'JobqueueBundle::layout.html.twig' %} 138 | {% block title "Jobqueue Status Control Center" %} 139 | {% block body %} 140 | 141 | 158 | 159 |
160 |

Jobqueue Status

161 |
162 |

Schedule Queue

163 | {% if scheduleQueue.totalJobs %} 164 |
    165 | {% for job in scheduleQueue %} 166 |
  • 167 |

    {{ job.name }}

    168 |
      169 | Type 170 |
    • {{ job.type }}
    • 171 | Schedule 172 |
    • {{ job.schedule }}
    • 173 | {% if job.lastRunDate is defined %} 174 | Last Run 175 |
    • {{ job.lastRunDate|date("m/d/Y") }}
    • 176 | {% endif %} 177 |
    178 |
  • 179 | {% endfor %} 180 |
181 | {% else %} 182 |

No jobs found in queue

183 | {% endif %} 184 |
185 |
186 | 187 | ### Command ### 188 | This bundle comes with a number of commands but the only truly important command is the Command\RunCommand.php script. 189 | 190 | To run this command from the smfony command console simply enter the following on your command line from your root directory: 191 | 192 | jesse@picard:~/ninethousand.org$ php app/console jobqueue:run 193 | 194 | You can see that there's not alot going on here, it's simply a call to the control service's run() method, which cycles through all the queues and runs pending jobs. 195 | 196 | namespace NineThousand\Bundle\NineThousandJobqueueBundle\Command; 197 | 198 | use Symfony\Component\Console\Input\InputArgument; 199 | use Symfony\Component\Console\Input\InputOption; 200 | use Symfony\Component\Console\Input\InputInterface; 201 | use Symfony\Component\Console\Output\OutputInterface; 202 | use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; 203 | 204 | class RunCommand extends ContainerAwareCommand 205 | { 206 | 207 | protected function configure() 208 | { 209 | $this->setName('jobqueue:run') 210 | ->setDescription('Cycles through the jobqueue only once') 211 | ->setHelp(<<{$this->getName()} Run the Jobqueue one time through. 213 | EOT 214 | ); 215 | } 216 | 217 | protected function execute(InputInterface $input, OutputInterface $output) 218 | { 219 | $this->getContainer()->get('jobqueue.control')->run(); 220 | } 221 | } 222 | 223 | #### Cron #### 224 | To persistently run your job queue you could run the command every 10 seconds (or any interval) as a cron job: 225 | 226 | */10 * * * * (cd /home/jesse/ninethousand.org && php app/console jobqueue:run > /dev/null 2>&1 ) 227 | 228 | #### Run as a Daemon #### 229 | You could also run the job queue as a system daemon 230 | 231 | CodeMemeDaemonBundle is a wrapper for the PEAR library System_Daemon which was created by Kevin Vanzonneveld. 232 | 233 | This will enable you to install the symfony bundle and easily convert your Symfony2 console scripts into system daemons. 234 | 235 | pcntl is required to be configured in your PHP binary to use this. On my Ubuntu server I was able to install pcntl easily with the following command: 236 | 237 | sudo apt-get install php-5.3-pcntl-zend-server 238 | 239 | ##### install CodeMemeDaemonBundle ##### 240 | You can add the Daemonbundle to your deps file for easy installation 241 | 242 | [CodeMemeDaemonBundle] 243 | git=http://github.com/CodeMeme/CodeMemeDaemonBundle.git 244 | target=/bundles/CodeMeme/Bundle/CodeMemeDaemonBundle 245 | 246 | Add the following to your autoload.php file: 247 | 248 | $loader->registerNamespaces(array( 249 | //... 250 | 'CodeMeme' => __DIR__.'/../vendor/bundles', 251 | )); 252 | 253 | Add The CodeMemeDaemonBundle to your kernel bootstrap sequence 254 | 255 | public function registerBundles() 256 | { 257 | $bundles = array( 258 | //... 259 | new CodeMeme\Bundle\CodeMemeDaemonBundle\CodeMemeDaemonBundle(), 260 | ); 261 | //... 262 | 263 | return $bundles; 264 | } 265 | 266 | ### config.yml ### 267 | By Default, system daemons have a sensible configuration. If you need to change any configuration setting , you could do it by adding this configuration to your project config. Only the values that need to be changed should be added, the bundle extension will merge your daemon configs into its defaults. 268 | 269 | app/config.yml 270 | 271 | #CodeMemeDaemonBundle Configuration Example 272 | code_meme_daemon: 273 | daemons: 274 | #creates a daemon using default options 275 | jobqueue: ~ 276 | 277 | #an example of all the available options 278 | explicitjobqueue: 279 | appName: example 280 | appDir: %kernel.root_dir% 281 | appDescription: Example of how to configure the DaemonBundle 282 | logLocation: %kernel.logs_dir%/%kernel.environment%.example.log 283 | authorName: Jesse Greathouse 284 | authorEmail: jesse.greathouse@gmail.com 285 | appPidLocation: %kernel.cache_dir%/example/example.pid 286 | sysMaxExecutionTime: 0 287 | sysMaxInputTime: 0 288 | sysMemoryLimit: 1024M 289 | appUser: apache 290 | appGroup: apache 291 | appRunAsGID: 1000 292 | appRunAsUID: 1000 293 | 294 | #### RunAs #### 295 | You can run the daemon as a different user or group depending on what is best for your application. By default it will resolve the user and group of the user who is running the daemon from the command console, but if you want to run as a different user you can use the appUser, appGroup or appRunAsGID, appRunAsUID options. Remember if you need to run as a different user you must start the daemon as sudo or a superuser. 296 | 297 | To find out the group and user id of a specific user you can use the following commands. 298 | 299 | jesse@picard:~/ninethousand.org$ id -u www-data 300 | jesse@picard:~/ninethousand.org$ id -g www-data 301 | 302 | Now you can simply start and stop the Jobqueue Daemon with the following commands 303 | 304 | jesse@picard:~/ninethousand.org$ php app/console jobqueue:start 305 | 306 | jesse@picard:~/ninethousand.org$ php app/console jobqueue:stop 307 | 308 | jesse@picard:~/ninethousand.org$ php app/console jobqueue:restart 309 | -------------------------------------------------------------------------------- /Repository/JobRepository.php: -------------------------------------------------------------------------------- 1 | getEntityManager()->createQueryBuilder(); 27 | $qb->select('j'); 28 | $qb->from('NineThousandJobqueueBundle:Job', 'j'); 29 | 30 | //scheduled filter 31 | if (!$query['scheduled']) { 32 | $qb->andWhere('j.schedule is NULL'); 33 | } else { 34 | $qb->andWhere('j.schedule is not NULL'); 35 | } 36 | 37 | //status filter { 38 | if (FALSE !== strpos($query['status'], 'f') || 39 | ($query['status'] === 0)) { 40 | $qb->andWhere('j.status = :status'); 41 | $params['status'] = 'fail'; 42 | } else if (FALSE !== strpos($query['status'], 's') || 43 | ($query['status']===1)) { 44 | $qb->andWhere('j.status = :status'); 45 | $params['status'] = 'success'; 46 | } else if (FALSE !== strpos($query['status'], 'r')) { 47 | $qb->andWhere('j.retry = :retry'); 48 | $qb->andWhere('j.attempts < j.maxRetries'); 49 | $params['retry'] = 1; 50 | } else if (FALSE !== strpos($query['status'], 'p')) { 51 | $qb->andWhere('j.retry <> :retry'); 52 | $qb->andWhere('j.active = :active'); 53 | $params['retry'] = 1; 54 | $params['active'] = 1; 55 | } 56 | 57 | $qb->orderBy('j.lastrunDate ', $order) 58 | ->setParameters($params); 59 | 60 | $q = $qb->getQuery(); 61 | if (null !== $query['limit']) { 62 | $countQuery = clone $q; 63 | $countQuery->setParameters($q->getParameters()); 64 | 65 | $q->setMaxResults($query['limit']); 66 | if (null !== $query['offset']) { 67 | $q->setFirstResult($query['offset']); 68 | } 69 | 70 | $result['totalResults'] = count($countQuery->getResult()); 71 | } 72 | 73 | $result['entities'] = $q->getResult(); 74 | if (!$result['totalResults']) { 75 | $result['totalResults'] = count($result['entities']); 76 | } 77 | 78 | return $result; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Resources/config/example.config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | #Jobqueue Configuration 3 | jobqueue: 4 | 5 | #The control service configuration 6 | control: 7 | class: "NineThousand\Jobqueue\Service\JobqueueControl" 8 | 9 | #Job component configuration 10 | job: 11 | class: "NineThousand\Jobqueue\Job\StandardJob" 12 | 13 | #adapter configuration includes job adapter and job controll mapping 14 | adapter: 15 | class: "NineThousand\Jobqueue\Vendor\Doctrine\Adapter\Queue\DoctrineQueueAdapter" 16 | options: 17 | job_entity_class: "NineThousand\Bundle\NineThousandJobqueueBundle\Entity\Job" 18 | job_adapter_class: "NineThousand\Bundle\NineThousandJobqueueBundle\Vendor\Doctrine\Adapter\Job\DoctrineJobAdapter" 19 | history_entity_class: "NineThousand\Bundle\NineThousandJobqueueBundle\Entity\History" 20 | history_adapter_class: "NineThousand\Jobqueue\Vendor\Doctrine\Adapter\History\DoctrineHistoryAdapter" 21 | log_adapter_class: "NineThousand\Jobqueue\Vendor\Doctrine\Adapter\Log\MonologAdapter" 22 | jobcontrol: 23 | type_mapping: 24 | SymfonyConsoleJobControl: "NineThousand\Jobqueue\Vendor\Symfony2\Adapter\Job\Control\Symfony2ConsoleJobControl" 25 | 26 | #ui configuration 27 | ui: 28 | options: 29 | pagnation: 30 | limit: 40 31 | pages_before: 5 32 | pages_after: 5 33 | 34 | --- 35 | #CodeMemeDaemonBundle Configuration Example 36 | code_meme_daemon: 37 | daemons: 38 | #creates a daemon using default options 39 | jobqueue: ~ 40 | -------------------------------------------------------------------------------- /Resources/config/jobqueue.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | NineThousand\Jobqueue\Job\StandardJob 10 | 11 | 12 | NineThousand\Jobqueue\Service\JobqueueControl 13 | 14 | 15 | NineThousand\Jobqueue\Vendor\Doctrine\Adapter\Queue\DoctrineQueueAdapter 16 | 17 | 18 | 19 | NineThousand\Bundle\NineThousandJobqueueBundle\Entity\Job 20 | NineThousand\Bundle\NineThousandJobqueueBundle\Vendor\Doctrine\Adapter\Job\Symfony2DoctrineJobAdapter 21 | NineThousand\Bundle\NineThousandJobqueueBundle\Entity\History 22 | NineThousand\Jobqueue\Vendor\Doctrine\Adapter\History\DoctrineHistoryAdapter 23 | NineThousand\Jobqueue\Vendor\Doctrine\Adapter\Log\MonologAdapter 24 | 25 | 26 | NineThousand\Jobqueue\Vendor\Symfony2\Adapter\Job\Control\Symfony2ConsoleJobControl 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 40 35 | 5 36 | 5 37 | 38 | 39 | 40 | 41 | 42 | 43 | %jobqueue.job.class% 44 | %jobqueue.adapter.class% 45 | %jobqueue.adapter.options% 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /Resources/config/routing.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | NineThousandJobqueueBundle:Default:index 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Resources/config/routing/history.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | NineThousandJobqueueBundle:History:index 9 | 10 | 11 | -------------------------------------------------------------------------------- /Resources/config/routing/job.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | NineThousandJobqueueBundle:Job:index 9 | 10 | 11 | 12 | NineThousandJobqueueBundle:Job:show 13 | 14 | 15 | 16 | NineThousandJobqueueBundle:Job:new 17 | 18 | 19 | 20 | NineThousandJobqueueBundle:Job:create 21 | post 22 | 23 | 24 | 25 | NineThousandJobqueueBundle:Job:edit 26 | 27 | 28 | 29 | NineThousandJobqueueBundle:Job:update 30 | post 31 | 32 | 33 | 34 | NineThousandJobqueueBundle:Job:deactivate 35 | post 36 | 37 | 38 | 39 | NineThousandJobqueueBundle:Job:activate 40 | post 41 | 42 | 43 | 44 | NineThousandJobqueueBundle:Job:retry 45 | post 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /Resources/config/routing/scheduledjob.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | NineThousandJobqueueBundle:ScheduledJob:index 9 | 10 | 11 | 12 | NineThousandJobqueueBundle:ScheduledJob:show 13 | 14 | 15 | 16 | NineThousandJobqueueBundle:ScheduledJob:new 17 | 18 | 19 | 20 | NineThousandJobqueueBundle:ScheduledJob:create 21 | post 22 | 23 | 24 | 25 | NineThousandJobqueueBundle:ScheduledJob:edit 26 | 27 | 28 | 29 | NineThousandJobqueueBundle:ScheduledJob:update 30 | post 31 | 32 | 33 | 34 | NineThousandJobqueueBundle:ScheduledJob:deactivate 35 | post 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Resources/public/css/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ninethousand/NineThousandJobqueueBundle/d5629804bc89593868e671c6ce0ccbb76ee907cd/Resources/public/css/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png -------------------------------------------------------------------------------- /Resources/public/css/smoothness/images/ui-bg_flat_75_ffffff_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ninethousand/NineThousandJobqueueBundle/d5629804bc89593868e671c6ce0ccbb76ee907cd/Resources/public/css/smoothness/images/ui-bg_flat_75_ffffff_40x100.png -------------------------------------------------------------------------------- /Resources/public/css/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ninethousand/NineThousandJobqueueBundle/d5629804bc89593868e671c6ce0ccbb76ee907cd/Resources/public/css/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png -------------------------------------------------------------------------------- /Resources/public/css/smoothness/images/ui-bg_glass_65_ffffff_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ninethousand/NineThousandJobqueueBundle/d5629804bc89593868e671c6ce0ccbb76ee907cd/Resources/public/css/smoothness/images/ui-bg_glass_65_ffffff_1x400.png -------------------------------------------------------------------------------- /Resources/public/css/smoothness/images/ui-bg_glass_75_dadada_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ninethousand/NineThousandJobqueueBundle/d5629804bc89593868e671c6ce0ccbb76ee907cd/Resources/public/css/smoothness/images/ui-bg_glass_75_dadada_1x400.png -------------------------------------------------------------------------------- /Resources/public/css/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ninethousand/NineThousandJobqueueBundle/d5629804bc89593868e671c6ce0ccbb76ee907cd/Resources/public/css/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png -------------------------------------------------------------------------------- /Resources/public/css/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ninethousand/NineThousandJobqueueBundle/d5629804bc89593868e671c6ce0ccbb76ee907cd/Resources/public/css/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png -------------------------------------------------------------------------------- /Resources/public/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ninethousand/NineThousandJobqueueBundle/d5629804bc89593868e671c6ce0ccbb76ee907cd/Resources/public/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png -------------------------------------------------------------------------------- /Resources/public/css/smoothness/images/ui-icons_222222_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ninethousand/NineThousandJobqueueBundle/d5629804bc89593868e671c6ce0ccbb76ee907cd/Resources/public/css/smoothness/images/ui-icons_222222_256x240.png -------------------------------------------------------------------------------- /Resources/public/css/smoothness/images/ui-icons_2e83ff_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ninethousand/NineThousandJobqueueBundle/d5629804bc89593868e671c6ce0ccbb76ee907cd/Resources/public/css/smoothness/images/ui-icons_2e83ff_256x240.png -------------------------------------------------------------------------------- /Resources/public/css/smoothness/images/ui-icons_454545_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ninethousand/NineThousandJobqueueBundle/d5629804bc89593868e671c6ce0ccbb76ee907cd/Resources/public/css/smoothness/images/ui-icons_454545_256x240.png -------------------------------------------------------------------------------- /Resources/public/css/smoothness/images/ui-icons_888888_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ninethousand/NineThousandJobqueueBundle/d5629804bc89593868e671c6ce0ccbb76ee907cd/Resources/public/css/smoothness/images/ui-icons_888888_256x240.png -------------------------------------------------------------------------------- /Resources/public/css/smoothness/images/ui-icons_cd0a0a_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ninethousand/NineThousandJobqueueBundle/d5629804bc89593868e671c6ce0ccbb76ee907cd/Resources/public/css/smoothness/images/ui-icons_cd0a0a_256x240.png -------------------------------------------------------------------------------- /Resources/public/css/style.css: -------------------------------------------------------------------------------- 1 | html, .ui-widget, .ui-accordion-header { 2 | font-family: Arial, Georgia, serif; 3 | font-size: 14px; 4 | } 5 | ul {list-style-type: none; padding-left:0px} 6 | 7 | .jobdata li { 8 | display: inline-block; 9 | padding-left: 2px; 10 | padding-right: 35px; 11 | padding-bottom: 0px; 12 | padding-top: 0px; 13 | } 14 | 15 | .pagination li { 16 | display: inline-block; 17 | } 18 | 19 | .emptyqueue { 20 | padding-left: 20px; 21 | } 22 | 23 | #wrapper {position: absolute;top:5px;width:99%;margin 0 5px 0 5px;padding 0;} 24 | #wrapper #rightPanel {float:right; width: 84%;} 25 | #wrapper #rightPanel #history {display:inline-block; width: 100%} 26 | #wrapper #rightPanel #jobs td.hidden {display:none; width:100%;} 27 | #wrapper #rightPanel #jobs td.hidden .jobdetails {width:20%; float:left;} 28 | #wrapper #rightPanel #jobs td.hidden .jobhistory {width:80%;overflow-y:auto;float:right;max-height:400px;} 29 | #wrapper #rightPanel #jobs td.hidden .jobhistory th {text-align:left;} 30 | #wrapper #rightPanel #jobs div.record_action_forms {min-width:350px;} 31 | #wrapper #rightPanel #jobs td {min-width:110px; margin:10px;} 32 | #wrapper #rightPanel #footer {clear:both;display:block;padding 5px;} 33 | #wrapper #leftPanel {float:left;width:15%;} 34 | #wrapper #leftPanel #menu {width:170px;vertical-align:top;padding 5px;} 35 | #wrapper #leftPanel #menu a.single {display:inline-block;width:167px;} 36 | #wrapper #leftPanel #menu a.submenu {display:inline-block;width:167px;} 37 | #wrapper #leftPanel #menu a.split {display:inline-block;width:130px;} 38 | #wrapper #leftPanel #menu #jobmenu {display:none;position:fixed;z-index:1;top:80px;left:30px;width:167px;border:none;-moz-box-shadow: 3px 3px 10px #999;-webkit-box-shadow: 3px 3px 10px #999;} 39 | 40 | 41 | #schedulequeue h2, 42 | #activequeue h2, 43 | #retryqueue h2 { 44 | padding-left: 25px; 45 | padding-top: 10px; 46 | height: 30px; 47 | } 48 | 49 | -------------------------------------------------------------------------------- /Resources/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ninethousand/NineThousandJobqueueBundle/d5629804bc89593868e671c6ce0ccbb76ee907cd/Resources/public/favicon.ico -------------------------------------------------------------------------------- /Resources/public/images/gear-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ninethousand/NineThousandJobqueueBundle/d5629804bc89593868e671c6ce0ccbb76ee907cd/Resources/public/images/gear-icon.png -------------------------------------------------------------------------------- /Resources/public/javascripts/jobqueue.js: -------------------------------------------------------------------------------- 1 | jQuery(function($) { 2 | 3 | var attachHandler = function () { 4 | $.each(AjaxControl, function(key, value) { 5 | if (value['handl'] instanceof Array) { 6 | $(key).unbind(value['type']); 7 | $.each(value['handl'], function(i, handl) { 8 | $(key).bind(value['type'], handl); 9 | }); 10 | } else { 11 | $(key).unbind(value['type']).bind(value['type'], value['handl']); 12 | } 13 | }); 14 | }; 15 | 16 | var rightPanelLink = function() { 17 | // get url to load from rel 18 | var loadUrl = $(this).attr('href'); 19 | var loadContainer = 'div#rightPanel'; 20 | if(loadUrl) { 21 | if ($(this).hasClass('newpage')) 22 | { 23 | window.open(loadUrl); 24 | return false; 25 | } else { 26 | $(loadContainer).load(loadUrl, attachHandler); 27 | return false; 28 | } 29 | } 30 | return false; 31 | }; 32 | 33 | var submitHandler = function() { 34 | var submitUrl = $(this).attr('action'); 35 | 36 | if (submitUrl && $(this).attr('method') === 'post') { 37 | $.post(submitUrl, $(this).serialize(), function(data) { 38 | $('div#rightPanel').html(data); 39 | }); 40 | } 41 | return false; 42 | }; 43 | 44 | var submitRecordControlForm = function() { 45 | var val = $(this).attr('value'); 46 | if (document.forms[val]) { 47 | $("form[name=" + val + "]").trigger('submit'); 48 | } else { 49 | $('div#rightPanel').load(val, attachHandler); 50 | } 51 | return false; 52 | } 53 | 54 | var jobDetails = function() { 55 | var id = $(this).attr('title'); 56 | var loadUrl = $(this).attr('href'); 57 | var pieces = loadUrl.split('/'); 58 | var loadHistoryUrl = '/' + pieces[1] +'/history/?job=' + id; 59 | var row = $(this).parent().parent().next().children('td.hidden'); 60 | if (row.css('display') != 'none') { 61 | row.css('display', 'none'); 62 | } else { 63 | var detailsContainer = row.children(":first"); 64 | var historyContainer = row.children(":last"); 65 | $(detailsContainer).load(loadUrl, attachHandler); 66 | $(historyContainer).load(loadHistoryUrl, attachHandler); 67 | row.toggle('slow') 68 | } 69 | return false; 70 | } 71 | 72 | //this object has to be defined near the bottom so the functions will be defined 73 | var AjaxControl = { 74 | '.record_action input:checkbox' : {type : 'click', handl : submitRecordControlForm}, 75 | '#jobs a.jobid' : {type : 'click', handl : jobDetails} 76 | }; 77 | 78 | $(document).ready(attachHandler); 79 | }); 80 | -------------------------------------------------------------------------------- /Resources/public/javascripts/jquery.livequery.js: -------------------------------------------------------------------------------- 1 | /*! Copyright (c) 2008 Brandon Aaron (http://brandonaaron.net) 2 | * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) 3 | * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses. 4 | * 5 | * Version: 1.0.3 6 | * Requires jQuery 1.1.3+ 7 | * Docs: http://docs.jquery.com/Plugins/livequery 8 | */ 9 | 10 | (function($) { 11 | 12 | $.extend($.fn, { 13 | livequery: function(type, fn, fn2) { 14 | var self = this, q; 15 | 16 | // Handle different call patterns 17 | if ($.isFunction(type)) 18 | fn2 = fn, fn = type, type = undefined; 19 | 20 | // See if Live Query already exists 21 | $.each( $.livequery.queries, function(i, query) { 22 | if ( self.selector == query.selector && self.context == query.context && 23 | type == query.type && (!fn || fn.$lqguid == query.fn.$lqguid) && (!fn2 || fn2.$lqguid == query.fn2.$lqguid) ) 24 | // Found the query, exit the each loop 25 | return (q = query) && false; 26 | }); 27 | 28 | // Create new Live Query if it wasn't found 29 | q = q || new $.livequery(this.selector, this.context, type, fn, fn2); 30 | 31 | // Make sure it is running 32 | q.stopped = false; 33 | 34 | // Run it immediately for the first time 35 | q.run(); 36 | 37 | // Contnue the chain 38 | return this; 39 | }, 40 | 41 | expire: function(type, fn, fn2) { 42 | var self = this; 43 | 44 | // Handle different call patterns 45 | if ($.isFunction(type)) 46 | fn2 = fn, fn = type, type = undefined; 47 | 48 | // Find the Live Query based on arguments and stop it 49 | $.each( $.livequery.queries, function(i, query) { 50 | if ( self.selector == query.selector && self.context == query.context && 51 | (!type || type == query.type) && (!fn || fn.$lqguid == query.fn.$lqguid) && (!fn2 || fn2.$lqguid == query.fn2.$lqguid) && !this.stopped ) 52 | $.livequery.stop(query.id); 53 | }); 54 | 55 | // Continue the chain 56 | return this; 57 | } 58 | }); 59 | 60 | $.livequery = function(selector, context, type, fn, fn2) { 61 | this.selector = selector; 62 | this.context = context || document; 63 | this.type = type; 64 | this.fn = fn; 65 | this.fn2 = fn2; 66 | this.elements = []; 67 | this.stopped = false; 68 | 69 | // The id is the index of the Live Query in $.livequery.queries 70 | this.id = $.livequery.queries.push(this)-1; 71 | 72 | // Mark the functions for matching later on 73 | fn.$lqguid = fn.$lqguid || $.livequery.guid++; 74 | if (fn2) fn2.$lqguid = fn2.$lqguid || $.livequery.guid++; 75 | 76 | // Return the Live Query 77 | return this; 78 | }; 79 | 80 | $.livequery.prototype = { 81 | stop: function() { 82 | var query = this; 83 | 84 | if ( this.type ) 85 | // Unbind all bound events 86 | this.elements.unbind(this.type, this.fn); 87 | else if (this.fn2) 88 | // Call the second function for all matched elements 89 | this.elements.each(function(i, el) { 90 | query.fn2.apply(el); 91 | }); 92 | 93 | // Clear out matched elements 94 | this.elements = []; 95 | 96 | // Stop the Live Query from running until restarted 97 | this.stopped = true; 98 | }, 99 | 100 | run: function() { 101 | // Short-circuit if stopped 102 | if ( this.stopped ) return; 103 | var query = this; 104 | 105 | var oEls = this.elements, 106 | els = $(this.selector, this.context), 107 | nEls = els.not(oEls); 108 | 109 | // Set elements to the latest set of matched elements 110 | this.elements = els; 111 | 112 | if (this.type) { 113 | // Bind events to newly matched elements 114 | nEls.bind(this.type, this.fn); 115 | 116 | // Unbind events to elements no longer matched 117 | if (oEls.length > 0) 118 | $.each(oEls, function(i, el) { 119 | if ( $.inArray(el, els) < 0 ) 120 | $.event.remove(el, query.type, query.fn); 121 | }); 122 | } 123 | else { 124 | // Call the first function for newly matched elements 125 | nEls.each(function() { 126 | query.fn.apply(this); 127 | }); 128 | 129 | // Call the second function for elements no longer matched 130 | if ( this.fn2 && oEls.length > 0 ) 131 | $.each(oEls, function(i, el) { 132 | if ( $.inArray(el, els) < 0 ) 133 | query.fn2.apply(el); 134 | }); 135 | } 136 | } 137 | }; 138 | 139 | $.extend($.livequery, { 140 | guid: 0, 141 | queries: [], 142 | queue: [], 143 | running: false, 144 | timeout: null, 145 | 146 | checkQueue: function() { 147 | if ( $.livequery.running && $.livequery.queue.length ) { 148 | var length = $.livequery.queue.length; 149 | // Run each Live Query currently in the queue 150 | while ( length-- ) 151 | $.livequery.queries[ $.livequery.queue.shift() ].run(); 152 | } 153 | }, 154 | 155 | pause: function() { 156 | // Don't run anymore Live Queries until restarted 157 | $.livequery.running = false; 158 | }, 159 | 160 | play: function() { 161 | // Restart Live Queries 162 | $.livequery.running = true; 163 | // Request a run of the Live Queries 164 | $.livequery.run(); 165 | }, 166 | 167 | registerPlugin: function() { 168 | $.each( arguments, function(i,n) { 169 | // Short-circuit if the method doesn't exist 170 | if (!$.fn[n]) return; 171 | 172 | // Save a reference to the original method 173 | var old = $.fn[n]; 174 | 175 | // Create a new method 176 | $.fn[n] = function() { 177 | // Call the original method 178 | var r = old.apply(this, arguments); 179 | 180 | // Request a run of the Live Queries 181 | $.livequery.run(); 182 | 183 | // Return the original methods result 184 | return r; 185 | } 186 | }); 187 | }, 188 | 189 | run: function(id) { 190 | if (id != undefined) { 191 | // Put the particular Live Query in the queue if it doesn't already exist 192 | if ( $.inArray(id, $.livequery.queue) < 0 ) 193 | $.livequery.queue.push( id ); 194 | } 195 | else 196 | // Put each Live Query in the queue if it doesn't already exist 197 | $.each( $.livequery.queries, function(id) { 198 | if ( $.inArray(id, $.livequery.queue) < 0 ) 199 | $.livequery.queue.push( id ); 200 | }); 201 | 202 | // Clear timeout if it already exists 203 | if ($.livequery.timeout) clearTimeout($.livequery.timeout); 204 | // Create a timeout to check the queue and actually run the Live Queries 205 | $.livequery.timeout = setTimeout($.livequery.checkQueue, 20); 206 | }, 207 | 208 | stop: function(id) { 209 | if (id != undefined) 210 | // Stop are particular Live Query 211 | $.livequery.queries[ id ].stop(); 212 | else 213 | // Stop all Live Queries 214 | $.each( $.livequery.queries, function(id) { 215 | $.livequery.queries[ id ].stop(); 216 | }); 217 | } 218 | }); 219 | 220 | // Register core DOM manipulation methods 221 | $.livequery.registerPlugin('append', 'prepend', 'after', 'before', 'wrap', 'attr', 'removeAttr', 'addClass', 'removeClass', 'toggleClass', 'empty', 'remove'); 222 | 223 | // Run Live Queries when the Document is ready 224 | $(function() { $.livequery.play(); }); 225 | 226 | 227 | // Save a reference to the original init method 228 | var init = $.prototype.init; 229 | 230 | // Create a new init method that exposes two new properties: selector and context 231 | $.prototype.init = function(a,c) { 232 | // Call the original init and save the result 233 | var r = init.apply(this, arguments); 234 | 235 | // Copy over properties if they exist already 236 | if (a && a.selector) 237 | r.context = a.context, r.selector = a.selector; 238 | 239 | // Set properties 240 | if ( typeof a == 'string' ) 241 | r.context = c || document, r.selector = a; 242 | 243 | // Return the result 244 | return r; 245 | }; 246 | 247 | // Give the init function the jQuery prototype for later instantiation (needed after Rev 4091) 248 | $.prototype.init.prototype = $.prototype; 249 | 250 | })(jQuery); -------------------------------------------------------------------------------- /Resources/views/Default/index.html.twig: -------------------------------------------------------------------------------- 1 | {% extends app.request.isXmlHttpRequest() ? 'NineThousandJobqueueBundle::ajax.html.twig' : 'NineThousandJobqueueBundle::layout.html.twig' %} 2 | {% block title "Jobqueue Status Control Center" %} 3 | {% block body %} 4 | 5 | 22 | 23 |
24 |

Jobqueue Status

25 |
26 |

Schedule Queue

27 | {% if scheduleQueue.totalJobs %} 28 |
    29 | {% for job in scheduleQueue %} 30 |
  • 31 |

    {{ job.name }}

    32 |
      33 | Type 34 |
    • {{ job.type }}
    • 35 | Schedule 36 |
    • {{ job.schedule }}
    • 37 | {% if job.lastRunDate is defined %} 38 | Last Run 39 |
    • {{ job.lastRunDate|date("m/d/Y") }}
    • 40 | {% endif %} 41 |
    42 |
  • 43 | {% endfor %} 44 |
45 | {% else %} 46 |

No jobs found in queue

47 | {% endif %} 48 |
49 | 50 |
51 |

Active Queue

52 | {% if activeQueue.totalJobs %} 53 |
    54 | {% for job in activeQueue %} 55 |
  • 56 |

    Job #{{ job.adapter.id }}

    57 |
      58 | Type 59 |
    • {{ job.type }}
    • 60 | Schedule 61 |
    • {{ job.schedule }}
    • 62 | {% if job.lastRunDate is defined %} 63 | Last Run 64 |
    • {{ job.lastRunDate|date("m/d/Y") }}
    • 65 | {% endif %} 66 |
    67 |
  • 68 | {% endfor %} 69 |
70 | {% else %} 71 |

No jobs found in queue

72 | {% endif %} 73 |
74 | 75 |
76 |

Retry Queue

77 | {% if retryQueue.totalJobs %} 78 |
    79 | {% for job in retryQueue %} 80 |
  • 81 |

    Job #{{ job.adapter.id }}

    82 |
      83 | Type 84 |
    • {{ job.type }}
    • 85 | Schedule 86 |
    • {{ job.schedule }}
    • 87 | {% if job.lastRunDate is defined %} 88 | Last Run 89 |
    • {{ job.lastRunDate|date("m/d/Y") }}
    • 90 | {% endif %} 91 |
    92 |
  • 93 | {% endfor %} 94 |
95 | {% else %} 96 |

No jobs found in queue

97 | {% endif %} 98 |
99 | 100 |
101 | 102 | {% endblock %} 103 | -------------------------------------------------------------------------------- /Resources/views/History/index.html.twig: -------------------------------------------------------------------------------- 1 | {% extends app.request.isXmlHttpRequest() ? 'NineThousandJobqueueBundle::ajax.html.twig' : 'NineThousandJobqueueBundle::layout.html.twig' %} 2 | 3 | {% block title "Jobqueue History" %} 4 | {% block body %} 5 |
6 | {% if not app.request.isXmlHttpRequest() %} 7 |

Job History

8 | {% endif %} 9 | {% if not app.request.isXmlHttpRequest() and pagination.last > 1 %} 10 | {% include "NineThousandJobqueueBundle::pagination.html.twig" %} 11 | {% endif %} 12 | {% if history.total %} 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | {% for entry in history %} 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | {% endfor %} 34 |
Job idJob TypeStatusTimestamp
{{ entry.jobId }}{{ entry.jobType }}{{ entry.status }}{{ entry.timestamp|date("Y-m-d g:ia") }}
{{ entry.message }}
35 | {% else %} 36 |

No jobs found in history

37 | {% endif %} 38 |
39 | {% if not app.request.isXmlHttpRequest() and pagination.last > 1 %} 40 | {% include "NineThousandJobqueueBundle::pagination.html.twig" %} 41 | {% endif %} 42 | {% endblock %} 43 | -------------------------------------------------------------------------------- /Resources/views/Job/index.html.twig: -------------------------------------------------------------------------------- 1 | {% extends app.request.isXmlHttpRequest() ? 'NineThousandJobqueueBundle::ajax.html.twig' : 'NineThousandJobqueueBundle::layout.html.twig' %} 2 | {% block title "Jobs" %} 3 | 4 | {% block body %} 5 | 6 | 7 | 16 |
17 | {% if not app.request.isXmlHttpRequest() %} 18 |

Job List

19 | {% endif %} 20 | {% if not app.request.isXmlHttpRequest() and pagination.last > 1 %} 21 | {% include "NineThousandJobqueueBundle::pagination.html.twig" %} 22 | {% endif %} 23 | 24 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | {% for entity in entities %} 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 94 | 95 | 96 | 100 | 101 | {% endfor %} 102 | 103 |
IdExecutableTypeStatusLastrundateParentActions
Job #{{ entity.id }}{{ entity.executable }}{{ entity.type }}{{ entity.status }}{{ entity.lastrunDate|date('Y-m-d H:i:s') }}{{ entity.parent }} 52 |
53 | 54 | 55 | 56 | 57 | 60 | 61 | 62 | 63 | 66 | 67 | 68 | 69 | 76 | 77 | 78 |
79 |
80 |
81 | {% set deactivate_form = forms[entity.id]['deactivate_form'] %} 82 | {{ form_widget(deactivate_form) }} 83 |
84 |
85 | {% set activate_form = forms[entity.id]['activate_form'] %} 86 | {{ form_widget(activate_form) }} 87 |
88 |
89 | {% set retry_form = forms[entity.id]['retry_form'] %} 90 | {{ form_widget(retry_form) }} 91 |
92 |
93 |
104 | {% if not app.request.isXmlHttpRequest() and pagination.last > 1 %} 105 | {% include "NineThousandJobqueueBundle::pagination.html.twig" %} 106 | {% endif %} 107 |
108 | 109 | {% endblock %} 110 | -------------------------------------------------------------------------------- /Resources/views/Job/new.html.twig: -------------------------------------------------------------------------------- 1 | {% extends app.request.isXmlHttpRequest() ? 'NineThousandJobqueueBundle::ajax.html.twig' : 'NineThousandJobqueueBundle::layout.html.twig' %} 2 | {% block title "Jobs" %} 3 | 4 | {% block body %} 5 | 111 |
112 |
113 |
114 | {{ form_errors(form) }} 115 |
116 |
117 |

Edit Job

118 | {% if deactivate_form is defined %} 119 |
120 | 123 | 124 | 125 | 126 | 129 | 130 | 131 | 132 | 139 | 140 | 141 |
142 | {% endif %} 143 | {{ form_row(form.type) }} 144 | {{ form_errors(form.type) }} 145 | {{ form_row(form.executable) }} 146 | {{ form_errors(form.executable) }} 147 | {{ form_row(form.retry) }} 148 | {{ form_errors(form.retry) }} 149 | {{ form_row(form.cooldown) }} 150 | {{ form_errors(form.cooldown) }} 151 | {{ form_row(form.maxRetries) }} 152 | {{ form_errors(form.maxRetries) }} 153 |
154 |
155 |

Parameters

156 |
    157 | {% for param in form.params %} 158 |
  • 159 |
    {{ form_widget(param.key) }} = {{ form_widget(param.value) }}
    160 |
    {{ form_errors(param.key) }} {{ form_errors(param.value) }}
    161 |
  • 162 | {% endfor %} 163 |
164 |
165 | 166 |
167 |
168 |
169 |

Arguments

170 |
    171 | {% for arg in form.args %} 172 |
  • 173 |
    {{ form_widget(arg.value) }}
    174 |
    {{ form_errors(arg.value) }}
    175 |
  • 176 | {% endfor %} 177 |
178 |
179 | 180 |
181 |
182 |
183 |

Tags

184 |
    185 | {% for tag in form.tags %} 186 |
  • 187 |
    {{ form_widget(tag.value) }}
    188 |
    {{ form_errors(tag.value) }}
    189 |
  • 190 | {% endfor %} 191 |
192 |
193 | 194 |
195 |
196 |
197 | {{ form_row(form._token) }} 198 | 199 |
200 |
201 |
202 | {% if deactivate_form is defined %} 203 |
204 |
205 | {{ form_widget(deactivate_form) }} 206 |
207 |
208 | {{ form_widget(activate_form) }} 209 |
210 |
211 | {{ form_widget(retry_form) }} 212 |
213 |
214 | {% endif %} 215 | {% endblock %} 216 | -------------------------------------------------------------------------------- /Resources/views/Job/show.html.twig: -------------------------------------------------------------------------------- 1 | {% extends app.request.isXmlHttpRequest() ? 'NineThousandJobqueueBundle::ajax.html.twig' : 'NineThousandJobqueueBundle::layout.html.twig' %} 2 | {% block title "Jobs" %} 3 | 4 | {% block body %} 5 | 12 | 13 | {% if not app.request.isXmlHttpRequest() %} 14 |

Job # {{ entity.id }}

15 |
16 | 19 | 20 | 21 | 22 | 25 | 26 | 27 | 28 | 35 | 36 | 37 |
38 | {% endif %} 39 | 40 | 41 | {% if not app.request.isXmlHttpRequest() %} 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | {% endif %} 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 |
Id {{ entity.id }}
Executable {{ entity.executable }}
Type {{ entity.type }}
Status {{ entity.status }}
Parent {{ entity.parent }}
Name {{ entity.name }}
Retry {{ entity.retry }}
Cooldown {{ entity.cooldown }}
Maxretries {{ entity.maxRetries }}
Attempts {{ entity.attempts }}
Createdate {{ entity.createDate|date('Y-m-d H:i:s') }}
Lastrundate {{ entity.lastrunDate|date('Y-m-d H:i:s') }}
Active {{ entity.active }}
Schedule {{ entity.schedule }}
74 | 75 | {% if not app.request.isXmlHttpRequest() %} 76 |
77 |
78 | {{ form_widget(deactivate_form) }} 79 |
80 |
81 | {{ form_widget(activate_form) }} 82 |
83 |
84 | {{ form_widget(retry_form) }} 85 |
86 |
87 | {% endif %} 88 | {% endblock %} 89 | -------------------------------------------------------------------------------- /Resources/views/ScheduledJob/index.html.twig: -------------------------------------------------------------------------------- 1 | {% extends app.request.isXmlHttpRequest() ? 'NineThousandJobqueueBundle::ajax.html.twig' : 'NineThousandJobqueueBundle::layout.html.twig' %} 2 | {% block title "Jobs" %} 3 | 4 | {% block body %} 5 | 6 | 7 | 16 |
17 | {% if not app.request.isXmlHttpRequest() %} 18 |

Scheduled Job List

19 | {% endif %} 20 | {% if not app.request.isXmlHttpRequest() and pagination.last > 1 %} 21 | {% include "NineThousandJobqueueBundle::pagination.html.twig" %} 22 | {% endif %} 23 | 24 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | {% for entity in entities %} 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 69 | 70 | 71 | 75 | 76 | {% endfor %} 77 | 78 |
NameExecutableTypeStatusLastrundateScheduleActions
{{ entity.name }}{{ entity.executable }}{{ entity.type }}{{ entity.status }}{{ entity.lastrunDate|date('Y-m-d H:i:s') }}{{ entity.schedule }} 52 |
53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 |
62 |
63 |
64 | {% set deactivate_form = forms[entity.id]['deactivate_form'] %} 65 | {{ form_widget(deactivate_form) }} 66 |
67 |
68 |
79 | {% if not app.request.isXmlHttpRequest() and pagination.last > 1 %} 80 | {% include "NineThousandJobqueueBundle::pagination.html.twig" %} 81 | {% endif %} 82 |
83 | 84 | {% endblock %} 85 | -------------------------------------------------------------------------------- /Resources/views/ScheduledJob/new.html.twig: -------------------------------------------------------------------------------- 1 | {% extends app.request.isXmlHttpRequest() ? 'NineThousandJobqueueBundle::ajax.html.twig' : 'NineThousandJobqueueBundle::layout.html.twig' %} 2 | {% block title "Jobs" %} 3 | 4 | {% block body %} 5 | 111 |
112 |
113 |
114 | {{ form_errors(form) }} 115 |
116 |
117 |

Edit Scheduled Job

118 | {{ form_row(form.name) }} 119 | {{ form_errors(form.name) }} 120 | {{ form_row(form.schedule) }} 121 | {{ form_errors(form.schedule) }} 122 | {{ form_row(form.type) }} 123 | {{ form_errors(form.type) }} 124 | {{ form_row(form.executable) }} 125 | {{ form_errors(form.executable) }} 126 |
127 |
128 |

Parameters

129 |
    130 | {% for param in form.params %} 131 |
  • 132 |
    {{ form_widget(param.key) }} = {{ form_widget(param.value) }}
    133 |
    {{ form_errors(param.key) }} {{ form_errors(param.value) }}
    134 |
  • 135 | {% endfor %} 136 |
137 |
138 | 139 |
140 |
141 |
142 |

Arguments

143 |
    144 | {% for arg in form.args %} 145 |
  • 146 |
    {{ form_widget(arg.value) }}
    147 |
    {{ form_errors(arg.value) }}
    148 |
  • 149 | {% endfor %} 150 |
151 |
152 | 153 |
154 |
155 |
156 |

Tags

157 |
    158 | {% for tag in form.tags %} 159 |
  • 160 |
    {{ form_widget(tag.value) }}
    161 |
    {{ form_errors(tag.value) }}
    162 |
  • 163 | {% endfor %} 164 |
165 |
166 | 167 |
168 |
169 |
170 | {{ form_row(form._token) }} 171 | 172 |
173 |
174 |
175 | {% endblock %} 176 | -------------------------------------------------------------------------------- /Resources/views/ScheduledJob/show.html.twig: -------------------------------------------------------------------------------- 1 | {% extends app.request.isXmlHttpRequest() ? 'NineThousandJobqueueBundle::ajax.html.twig' : 'NineThousandJobqueueBundle::layout.html.twig' %} 2 | {% block title "Jobs" %} 3 | 4 | {% block body %} 5 | 6 | 7 | 8 | {% if not app.request.isXmlHttpRequest() %} 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | {% endif %} 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 |
Id {{ entity.id }}
Executable {{ entity.executable }}
Type {{ entity.type }}
Status {{ entity.status }}
Parent {{ entity.parent }}
Name {{ entity.name }}
Retry {{ entity.retry }}
Cooldown {{ entity.cooldown }}
Maxretries {{ entity.maxRetries }}
Attempts {{ entity.attempts }}
Createdate {{ entity.createDate|date('Y-m-d H:i:s') }}
Lastrundate {{ entity.lastrunDate|date('Y-m-d H:i:s') }}
Active {{ entity.active }}
Schedule {{ entity.schedule }}
41 | {% endblock %} 42 | -------------------------------------------------------------------------------- /Resources/views/ajax.html.twig: -------------------------------------------------------------------------------- 1 | {% block body %}{% endblock %} 2 | 3 | -------------------------------------------------------------------------------- /Resources/views/footer.html.twig: -------------------------------------------------------------------------------- 1 |

2 | © ninethousand.org 3 | | jesse@ninethousand.org 4 |

5 | -------------------------------------------------------------------------------- /Resources/views/layout.html.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {% block title %}{% endblock %} 6 | 7 | 8 | 9 | {% block stylesheets %} 10 | 11 | 12 | {% endblock %} 13 | 14 | {% block javascripts %} 15 | 16 | 17 | 18 | 19 | 22 | {% endblock %} 23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 |
31 | {% include "NineThousandJobqueueBundle::menu.html.twig" %} 32 |
33 | 34 |
35 | {% block body %}{% endblock %} 36 | 39 |
40 |
41 | 42 | 43 | -------------------------------------------------------------------------------- /Resources/views/menu.html.twig: -------------------------------------------------------------------------------- 1 | 26 | 27 | 45 | -------------------------------------------------------------------------------- /Resources/views/pagination.html.twig: -------------------------------------------------------------------------------- 1 | {% if pagination.pages %} 2 | 9 | 28 | {% endif %} 29 | -------------------------------------------------------------------------------- /Tests/Controller/ArgControllerTest.php: -------------------------------------------------------------------------------- 1 | request('GET', '/jobqueue/arg//'); 17 | $this->assertTrue(200 === $client->getResponse()->getStatusCode()); 18 | $crawler = $client->click($crawler->selectLink('Create a new entry')->link()); 19 | 20 | // Fill in the form and submit it 21 | $form = $crawler->selectButton('Create')->form(array( 22 | 'arg[field_name]' => 'Test', 23 | // ... other fields to fill 24 | )); 25 | 26 | $client->submit($form); 27 | $crawler = $client->followRedirect(); 28 | 29 | // Check data in the show view 30 | $this->assertTrue($crawler->filter('td:contains("Test")')->count() > 0); 31 | 32 | // Edit the entity 33 | $crawler = $client->click($crawler->selectLink('Edit')->link()); 34 | 35 | $form = $crawler->selectButton('Edit')->form(array( 36 | 'arg[field_name]' => 'Foo', 37 | // ... other fields to fill 38 | )); 39 | 40 | $client->submit($form); 41 | $crawler = $client->followRedirect(); 42 | 43 | // Check the element contains an attribute with value equals "Foo" 44 | $this->assertTrue($crawler->filter('[value="Foo"]')->count() > 0); 45 | 46 | // Delete the entity 47 | $client->submit($crawler->selectButton('Delete')->form()); 48 | $crawler = $client->followRedirect(); 49 | 50 | // Check the entity has been delete on the list 51 | $this->assertNotRegExp('/Foo/', $client->getResponse()->getContent()); 52 | } 53 | */ 54 | } -------------------------------------------------------------------------------- /Tests/Controller/JobControllerTest.php: -------------------------------------------------------------------------------- 1 | request('GET', '/jobqueue/job//'); 17 | $this->assertTrue(200 === $client->getResponse()->getStatusCode()); 18 | $crawler = $client->click($crawler->selectLink('Create a new entry')->link()); 19 | 20 | // Fill in the form and submit it 21 | $form = $crawler->selectButton('Create')->form(array( 22 | 'job[field_name]' => 'Test', 23 | // ... other fields to fill 24 | )); 25 | 26 | $client->submit($form); 27 | $crawler = $client->followRedirect(); 28 | 29 | // Check data in the show view 30 | $this->assertTrue($crawler->filter('td:contains("Test")')->count() > 0); 31 | 32 | // Edit the entity 33 | $crawler = $client->click($crawler->selectLink('Edit')->link()); 34 | 35 | $form = $crawler->selectButton('Edit')->form(array( 36 | 'job[field_name]' => 'Foo', 37 | // ... other fields to fill 38 | )); 39 | 40 | $client->submit($form); 41 | $crawler = $client->followRedirect(); 42 | 43 | // Check the element contains an attribute with value equals "Foo" 44 | $this->assertTrue($crawler->filter('[value="Foo"]')->count() > 0); 45 | 46 | // Delete the entity 47 | $client->submit($crawler->selectButton('Delete')->form()); 48 | $crawler = $client->followRedirect(); 49 | 50 | // Check the entity has been delete on the list 51 | $this->assertNotRegExp('/Foo/', $client->getResponse()->getContent()); 52 | } 53 | */ 54 | } -------------------------------------------------------------------------------- /Tests/Controller/ParamControllerTest.php: -------------------------------------------------------------------------------- 1 | request('GET', '/jobqueue_param/'); 17 | $this->assertTrue(200 === $client->getResponse()->getStatusCode()); 18 | $crawler = $client->click($crawler->selectLink('Create a new entry')->link()); 19 | 20 | // Fill in the form and submit it 21 | $form = $crawler->selectButton('Create')->form(array( 22 | 'param[field_name]' => 'Test', 23 | // ... other fields to fill 24 | )); 25 | 26 | $client->submit($form); 27 | $crawler = $client->followRedirect(); 28 | 29 | // Check data in the show view 30 | $this->assertTrue($crawler->filter('td:contains("Test")')->count() > 0); 31 | 32 | // Edit the entity 33 | $crawler = $client->click($crawler->selectLink('Edit')->link()); 34 | 35 | $form = $crawler->selectButton('Edit')->form(array( 36 | 'param[field_name]' => 'Foo', 37 | // ... other fields to fill 38 | )); 39 | 40 | $client->submit($form); 41 | $crawler = $client->followRedirect(); 42 | 43 | // Check the element contains an attribute with value equals "Foo" 44 | $this->assertTrue($crawler->filter('[value="Foo"]')->count() > 0); 45 | 46 | // Delete the entity 47 | $client->submit($crawler->selectButton('Delete')->form()); 48 | $crawler = $client->followRedirect(); 49 | 50 | // Check the entity has been delete on the list 51 | $this->assertNotRegExp('/Foo/', $client->getResponse()->getContent()); 52 | } 53 | */ 54 | } -------------------------------------------------------------------------------- /Tests/Controller/TagControllerTest.php: -------------------------------------------------------------------------------- 1 | request('GET', '/jobqueue_tag/'); 17 | $this->assertTrue(200 === $client->getResponse()->getStatusCode()); 18 | $crawler = $client->click($crawler->selectLink('Create a new entry')->link()); 19 | 20 | // Fill in the form and submit it 21 | $form = $crawler->selectButton('Create')->form(array( 22 | 'tag[field_name]' => 'Test', 23 | // ... other fields to fill 24 | )); 25 | 26 | $client->submit($form); 27 | $crawler = $client->followRedirect(); 28 | 29 | // Check data in the show view 30 | $this->assertTrue($crawler->filter('td:contains("Test")')->count() > 0); 31 | 32 | // Edit the entity 33 | $crawler = $client->click($crawler->selectLink('Edit')->link()); 34 | 35 | $form = $crawler->selectButton('Edit')->form(array( 36 | 'tag[field_name]' => 'Foo', 37 | // ... other fields to fill 38 | )); 39 | 40 | $client->submit($form); 41 | $crawler = $client->followRedirect(); 42 | 43 | // Check the element contains an attribute with value equals "Foo" 44 | $this->assertTrue($crawler->filter('[value="Foo"]')->count() > 0); 45 | 46 | // Delete the entity 47 | $client->submit($crawler->selectButton('Delete')->form()); 48 | $crawler = $client->followRedirect(); 49 | 50 | // Check the entity has been delete on the list 51 | $this->assertNotRegExp('/Foo/', $client->getResponse()->getContent()); 52 | } 53 | */ 54 | } -------------------------------------------------------------------------------- /Tests/Fixtures/History/LoadHistoryTestData.php: -------------------------------------------------------------------------------- 1 | data as $record) { 16 | $testHistory = new History; 17 | $testHistory->setTimestamp(new \DateTime); 18 | foreach ($record as $key => $val) { 19 | if ($key == 'job') { 20 | $job = new Job; 21 | $job->setCreateDate(new \DateTime); 22 | foreach ($val as $k => $v) { 23 | $job->{'set'.ucwords($k)}($v); 24 | } 25 | $val = $job; 26 | $manager->persist($val); 27 | unset($job); 28 | } 29 | $testHistory->{'set'.ucwords($key)}($val); 30 | } 31 | $manager->persist($testHistory); 32 | array_push($result, $testHistory); 33 | $manager->flush(); 34 | unset($testHistory); 35 | } 36 | return $result; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Tests/Fixtures/History/LoadStandardHistoryTestData.php: -------------------------------------------------------------------------------- 1 | data as $record) { 14 | $testJob = new Job; 15 | $testJob->setCreateDate(new \DateTime); 16 | foreach ($record as $key => $val) { 17 | $testJob->{'set'.ucwords($key)}($val); 18 | } 19 | $manager->persist($testJob); 20 | array_push($result, $testJob); 21 | $manager->flush(); 22 | unset($testJob); 23 | } 24 | return $result; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Tests/Fixtures/Job/LoadJobqueueControlTestData.php: -------------------------------------------------------------------------------- 1 | kernel = $kernel; 18 | } 19 | 20 | public function setData($data) 21 | { 22 | $this->data = $data; 23 | } 24 | 25 | public function getData() 26 | { 27 | return $this->data; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Tests/History/StandardHistoryTest.php: -------------------------------------------------------------------------------- 1 | array( 23 | array( 24 | 'job' => array( 25 | 'executable' => 'console jobqueue:test1', 26 | 'type' => 'SymfonyConsoleJobControl', 27 | 'active' => 1, 28 | 'status' => 'success', 29 | ), 30 | 'status' => 'success', 31 | 'message' => 'History Test 1', 32 | 'severity' => 'debug', 33 | 'active' => 1, 34 | ), 35 | array( 36 | 'job' => array( 37 | 'executable' => 'console jobqueue:test2', 38 | 'type' => 'SymfonyConsoleJobControl', 39 | 'active' => 1, 40 | 'status' => 'success', 41 | ), 42 | 'status' => 'success', 43 | 'message' => 'History Test 2', 44 | 'severity' => 'debug', 45 | 'active' => 1, 46 | ), 47 | array( 48 | 'job' => array( 49 | 'executable' => 'console jobqueue:test3', 50 | 'type' => 'SymfonyConsoleJobControl', 51 | 'active' => 1, 52 | 'status' => 'success', 53 | ), 54 | 'status' => 'success', 55 | 'message' => 'History Test 3', 56 | 'severity' => 'debug', 57 | 'active' => 1, 58 | ), 59 | array( 60 | 'job' => array( 61 | 'executable' => 'console jobqueue:test4', 62 | 'type' => 'SymfonyConsoleJobControl', 63 | 'active' => 1, 64 | 'status' => 'success', 65 | ), 66 | 'status' => 'success', 67 | 'message' => 'History Test 4', 68 | 'severity' => 'debug', 69 | 'active' => 1, 70 | ), 71 | ), 72 | ); 73 | } 74 | 75 | public function setUp() 76 | { 77 | self::$kernel = $this->createKernel(array('environment' => 'test')); 78 | self::$kernel->boot(); 79 | $this->container = self::$kernel->getContainer(); 80 | $em = $this->container->get('doctrine')->getEntityManager(); 81 | $metadatas = $em->getMetadataFactory()->getAllMetadata(); 82 | $schemaTool = new SchemaTool($em); 83 | $schemaTool->dropDatabase(); 84 | $schemaTool->createSchema($metadatas); 85 | } 86 | 87 | public function tearDown() 88 | { 89 | unset($this->container); 90 | } 91 | 92 | /** 93 | * @dataProvider historyProvider 94 | * @covers NineThousand\Bundle\NineThousandJobqueueBundle\Model\History\StandardHistory::factory 95 | */ 96 | public function testFactory($history1, $history2, $history3, $history4) 97 | { 98 | $em = $this->container->get('doctrine')->getEntityManager(); 99 | $testHistory = array($history1, $history2, $history3, $history4); 100 | 101 | $fixtures = new LoadStandardHistoryTestData(self::$kernel); 102 | $fixtures->setData($testHistory); 103 | $this->data = $fixtures->load($em); 104 | 105 | $queueOptions = $this->container->getParameter('jobqueue.adapter.options'); 106 | $historyAdapterClass = $queueOptions['history_adapter_class']; 107 | $historyAdapter = new $historyAdapterClass($this->container->getParameter('jobqueue.adapter.options'), 108 | $this->container->get('doctrine')->getEntityManager()); 109 | 110 | $this->history = StandardHistory::factory($historyAdapter); 111 | 112 | $this->assertTrue(($this->history->count() >= 4), ""); 113 | } 114 | 115 | 116 | 117 | } 118 | -------------------------------------------------------------------------------- /Tests/Service/JobqueueControlTest.php: -------------------------------------------------------------------------------- 1 | array( 21 | array( 22 | 'executable' => 'console jobqueue:test1', 23 | 'type' => 'SymfonyConsoleJobControl', 24 | 'active' => 1, 25 | ), 26 | array( 27 | 'executable' => 'console jobqueue:test2', 28 | 'type' => 'SymfonyConsoleJobControl', 29 | 'active' => 1, 30 | ), 31 | array( 32 | 'name' => 'Test Scheduled Job', 33 | 'executable' => 'console jobqueue:test3', 34 | 'type' => 'SymfonyConsoleJobControl', 35 | 'active' => 0, 36 | 'lastrunDate' => new \DateTime('2011-06-12 15:30:00'), 37 | 'schedule' => '*/2 */2 * * *', 38 | ), 39 | array( 40 | 'executable' => 'console jobqueue:test4', 41 | 'type' => 'SymfonyConsoleJobControl', 42 | 'active' => 0, 43 | 'retry' => 1, 44 | 'attempts' => 1, 45 | 'status' => 'retry', 46 | 'maxRetries' => 2, 47 | 'cooldown' => 0, 48 | 'lastrunDate' => new \DateTime('2011-06-12'), 49 | ), 50 | ), 51 | ); 52 | } 53 | 54 | public function setUp() 55 | { 56 | self::$kernel = $this->createKernel(array('environment' => 'test')); 57 | self::$kernel->boot(); 58 | $this->container = self::$kernel->getContainer(); 59 | $em = $this->container->get('doctrine')->getEntityManager(); 60 | $metadatas = $em->getMetadataFactory()->getAllMetadata(); 61 | $schemaTool = new SchemaTool($em); 62 | $schemaTool->dropDatabase(); 63 | $schemaTool->createSchema($metadatas); 64 | } 65 | 66 | public function tearDown() 67 | { 68 | unset($this->container); 69 | } 70 | 71 | /** 72 | * @dataProvider jobsProvider 73 | * @covers NineThousand\Bundle\NineThousandJobqueueBundle\Service\JobqueueControl::run 74 | * @covers NineThousand\Bundle\NineThousandJobqueueBundle\Service\JobqueueControl::loadQueues 75 | * @covers NineThousand\Bundle\NineThousandJobqueueBundle\Service\JobqueueControl::runActiveQueue 76 | * @covers NineThousand\Bundle\NineThousandJobqueueBundle\Service\JobqueueControl::runRetryQueue 77 | * @covers NineThousand\Bundle\NineThousandJobqueueBundle\Service\JobqueueControl::runScheduleQueue 78 | * @covers NineThousand\Bundle\NineThousandJobqueueBundle\Model\Queue\ActiveQueue::factory 79 | * @covers NineThousand\Bundle\NineThousandJobqueueBundle\Model\Queue\ActiveQueue::adoptJob 80 | * @covers NineThousand\Bundle\NineThousandJobqueueBundle\Model\Queue\InactiveQueue::factory 81 | * @covers NineThousand\Bundle\NineThousandJobqueueBundle\Model\Queue\InactiveQueue::adoptJob 82 | * @covers NineThousand\Bundle\NineThousandJobqueueBundle\Model\Queue\RetryQueue::factory 83 | * @covers NineThousand\Bundle\NineThousandJobqueueBundle\Model\Queue\RetryQueue::adoptJob 84 | * @covers NineThousand\Bundle\NineThousandJobqueueBundle\Model\Queue\ScheduleQueue::factory 85 | * @covers NineThousand\Bundle\NineThousandJobqueueBundle\Model\Job\StandardJob::spawn 86 | * @covers NineThousand\Bundle\NineThousandJobqueueBundle\Model\Adapter\Job\DoctrineJobAdapter::spawn 87 | * @covers NineThousand\Bundle\NineThousandJobqueueBundle\Model\Adapter\Job\Control\SymfonyConsoleJobControl::getExecLine 88 | * @covers NineThousand\Bundle\NineThousandJobqueueBundle\Model\Adapter\Job\Control\SymfonyConsoleJobControl::run 89 | */ 90 | public function testRunActiveQueue($job1, $job2, $job3, $job4) 91 | { 92 | $em = $this->container->get('doctrine')->getEntityManager(); 93 | $testQueue = array($job1, $job2, $job3, $job4); 94 | foreach ($testQueue as &$record) { 95 | $record['executable'] = self::$kernel->getRootDir() . '/' . $record['executable']; 96 | } 97 | 98 | $fixtures = new LoadJobqueueControlTestData(self::$kernel); 99 | $fixtures->setData($testQueue); 100 | $this->data = $fixtures->load($em); 101 | 102 | $this->container->get('jobqueue.control')->run(); 103 | 104 | $result = array(); 105 | $repo = $em->getRepository('NineThousandJobqueueBundle:Job'); 106 | foreach ($this->data as $record) { 107 | if ($record->getSchedule() === NULL) { 108 | array_push($result, $repo->findOneById($record->getId())->getStatus()); 109 | } else { 110 | $child = $repo->findOneByParent($record->getId()); 111 | array_push($result, $child->getStatus()); 112 | } 113 | } 114 | $this->assertTrue((count(array_keys($result, 'success')) === 4), ""); 115 | } 116 | 117 | 118 | 119 | } 120 | -------------------------------------------------------------------------------- /Tests/Util/CronParserTest.php: -------------------------------------------------------------------------------- 1 | assertEquals('1', $cron->getSchedule('i')); 31 | $this->assertEquals('2-4', $cron->getSchedule('H')); 32 | $this->assertEquals('*', $cron->getSchedule('d')); 33 | $this->assertEquals('4', $cron->getSchedule('m')); 34 | $this->assertEquals('*/3', $cron->getSchedule('N')); 35 | $this->assertEquals('1 2-4 * 4 */3', $cron->getSchedule()); 36 | } 37 | 38 | /** 39 | * Data provider for cron schedule 40 | * 41 | * @return array 42 | */ 43 | public function scheduleProvider() 44 | { 45 | return array( 46 | // schedule, current time, last run, next run, is due 47 | 48 | // every 2 minutes, every 2 hours 49 | array('*/2 */2 * * *', '2010-08-10 21:47:27', '2010-08-10 15:30:00', '2010-08-10 22:00:00', true), 50 | // every 5 minutes 51 | array('*/5 * * * *', '2010-08-10 15:47:27', '2010-08-10 15:45:00', '2010-08-10 15:50:00', false), 52 | array('*/5 * * * *', '2010-08-10 15:50:43', '2010-08-10 15:45:00', '2010-08-10 15:55:00', true), 53 | // every minute 54 | array('* * * * *', '2010-08-10 21:50:37', '2010-08-10 21:00:00', '2010-08-10 21:51:00', true), 55 | array('* * * * *', '2011-02-02 22:15:52', '2011-02-02 22:15:44', '2011-02-02 22:16:00', false), 56 | // every day at 16:00 57 | array('0 16 * * *', '2010-08-10 15:50:37', '2010-08-09 16:00:34', '2010-08-10 16:00:00', false), 58 | array('0 16 * * *', '2010-08-10 16:00:43', '2010-08-09 16:00:34', '2010-08-11 16:00:00', true), 59 | // Minutes 7-9, every 9 days 60 | array('7-9 * */9 * *', '2010-08-10 22:02:33', '2010-08-10 22:01:33', '2010-08-18 00:07:00', false), 61 | // Minutes 12-19, every 3 hours, every 5 days, in June, on Sunday 62 | array('12-19 */3 */5 6 7', '2010-08-10 22:05:51', '2010-08-10 22:04:51', '2011-06-05 00:12:00', false), 63 | // 15th minute, of the second hour, every 15 days, in January, every Friday 64 | array('15 2 */15 1 */5', '2010-08-10 22:10:19', '2010-08-10 22:09:19', '2015-01-30 02:15:00', false), 65 | // 15th minute, of the second hour, every 15 days, in January, Tuesday-Friday 66 | array('15 2 */15 1 2-5', '2010-08-10 22:10:19', '2010-08-10 22:09:19', '2013-01-15 02:15:00', false), 67 | 68 | // 9th of month at 2:00am 69 | array('0 2 9 * *', '2011-04-12 15:17:28', '2011-04-12 02:01:04', '2011-05-09 02:00:00', false), 70 | array('0 2 9 * *', '2011-05-09 02:00:00', '2011-04-12 02:01:04', '2011-05-09 02:00:00', true), 71 | 72 | // check in the minute of next run 73 | // The difference: The first one is due, it has to run right now! 74 | // The second one has been processed for the current minute. 75 | array('50 * * * *', '2010-08-10 20:50:39', '2010-08-10 19:50:23', '2010-08-10 21:50:00', true), 76 | array('50 * * * *', '2010-08-10 20:50:56', '2010-08-10 20:50:43', '2010-08-10 21:50:00', false), 77 | 78 | // given by third party to check against 79 | array('* * * * *', '2011-02-12 23:33:47', '2011-02-12 23:34:00', '2011-02-12 23:35:00', false), 80 | 81 | // using relative test 82 | array('* * * * *', date('Y-m-d H:i:s'), date('Y-m-d H:i:s', strtotime('-10 minutes')), date('Y-m-d H:i:00', strtotime('+1 minute')), true), 83 | array('* * * * *', date('Y-m-d H:i:s'), date('Y-m-d H:i:00'), date('Y-m-d H:i:00', strtotime('+1 minute')), false), 84 | ); 85 | } 86 | 87 | /** 88 | * @covers CronParser::isDue 89 | * @covers CronParser::getNextScheduledDate 90 | * @dataProvider scheduleProvider 91 | */ 92 | public function testIsDueNextRun($schedule, $currentTime, $lastRun, $nextRun, $isDue) 93 | { 94 | $lastRun = new DateTime($lastRun); 95 | $currentTime = new DateTime($currentTime); 96 | $nextRun = new DateTime($nextRun); 97 | 98 | $cron = new CronParser($schedule); 99 | 100 | $this->assertEquals($isDue, $cron->isDue($lastRun, $currentTime)); 101 | $this->assertEquals($nextRun, $cron->getNextScheduledDate($lastRun, $currentTime)); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /Vendor/Doctrine/Adapter/Job/Symfony2DoctrineJobAdapter.php: -------------------------------------------------------------------------------- 1 | 13 | * @copyright 2011 NineThousand (https://github.com/organizations/NineThousand) 14 | * @license http://www.opensource.org/licenses/bsd-license.php New BSD Licence 15 | * @link https://github.com/NineThousand/ninethousand-jobqueue 16 | */ 17 | 18 | use NineThousand\Jobqueue\Adapter\Job\Exception\UnmappedAdapterTypeException; 19 | use NineThousand\Jobqueue\Adapter\Job\JobAdapterInterface; 20 | use NineThousand\Bundle\NineThousandJobqueueBundle\Entity\Job as JobEntity; 21 | use NineThousand\Bundle\NineThousandJobqueueBundle\Entity\History; 22 | use NineThousand\Bundle\NineThousandJobqueueBundle\Entity\Param; 23 | use NineThousand\Bundle\NineThousandJobqueueBundle\Entity\Tag; 24 | use NineThousand\Bundle\NineThousandJobqueueBundle\Entity\Arg; 25 | 26 | use Doctrine\ORM\EntityManager; 27 | 28 | class Symfony2DoctrineJobAdapter implements JobAdapterInterface 29 | { 30 | 31 | /** 32 | * @var Doctrine\ORM\EntityManager 33 | */ 34 | private $_em; 35 | 36 | /** 37 | * @var NineThousand\Bundle\NineThousandJobqueueBundle\Vendor\Doctrine\Entity\Job 38 | * 39 | */ 40 | private $_jobEntity; 41 | 42 | /** 43 | * @var string name of the jobcontrol adapter to use when running jobs 44 | */ 45 | private $_adapterClass; 46 | 47 | /** 48 | * @var array the options as defined by the service params 49 | */ 50 | private $_options = array(); 51 | 52 | /** 53 | * @var object the logger used in the application 54 | */ 55 | private $_logger; 56 | 57 | 58 | /** 59 | * Constructs the object. 60 | * 61 | * @param array $options 62 | * @param NineThousand\Bundle\NineThousandJobqueueBundle\Vendor\Doctrine\Entity\Job $jobEntity 63 | * @param Doctrine\ORM\EntityManager $em 64 | */ 65 | public function __construct(array $options, JobEntity $jobEntity, EntityManager $em, $logger) 66 | { 67 | $this->_em = $em; 68 | $this->_logger = $logger; 69 | $this->_jobEntity = $jobEntity; 70 | $this->_options = $options; 71 | try { 72 | $adapterClass = $this->_options['jobcontrol']['type_mapping'][$this->getType()]; 73 | } catch (UnmappedAdapterTypeException $e) {} 74 | $this->_adapterClass = new $adapterClass; 75 | $this->_adapterClass->setLogger($this->_logger); 76 | } 77 | 78 | /** 79 | * Duplicates a job with similar properties to the original. 80 | * 81 | * @return NineThousand\Jobqueue\Vendor\Doctrine\Adapter\Job\DoctrineJobAdapter 82 | */ 83 | public function spawn() 84 | { 85 | $entity = new JobEntity; 86 | $entity->setRetry($this->_jobEntity->getRetry()); 87 | $entity->setCooldown($this->_jobEntity->getCooldown()); 88 | $entity->setMaxRetries($this->_jobEntity->getMaxRetries()); 89 | $entity->setAttempts(0); 90 | $entity->setExecutable($this->_jobEntity->getExecutable()); 91 | $entity->setType($this->_jobEntity->getType()); 92 | $entity->setStatus(null); 93 | $entity->setCreateDate(new \DateTime("now")); 94 | $entity->setLastRunDate(null); 95 | $entity->setActive(1); 96 | $entity->setSchedule(null); 97 | $entity->setParent($this->_jobEntity->getId()); 98 | $this->_em->persist($entity); 99 | $this->_em->flush(); 100 | 101 | //instantiate duplicated job adapter and set params, args, tags 102 | $jobAdapter = new self($this->_options, $entity, $this->_em, $this->_logger); 103 | if (count($params = $this->getParams())) $jobAdapter->setParams($params); 104 | if (count($args = $this->getArgs())) $jobAdapter->setArgs($args); 105 | if (count($tags = $this->getTags())) $jobAdapter->setTags($tags); 106 | 107 | return $jobAdapter; 108 | } 109 | 110 | /** 111 | * Creates a new instantiation of a DoctrineJobAdapter object. 112 | * 113 | * @static 114 | * @return NineThousand\Jobqueue\Vendor\DoctrineAdapter\Job\DoctrineJobAdapter 115 | */ 116 | public static function factory($options, $entity, $em, $logger) 117 | { 118 | return new self($options, $entity, $em, $logger); 119 | } 120 | 121 | /** 122 | * Takes an array of command line, params and args and tranforms it into something that can be run. 123 | * 124 | * @param array $input 125 | * @return string 126 | */ 127 | public function getExecLine(array $input) 128 | { 129 | return $this->_adapterClass->getExecLine($input); 130 | } 131 | 132 | /** 133 | * Runs an arbitrary command line and returns a variable containing status, message, and severity. 134 | * 135 | * @param string $execLine 136 | * @return array 137 | */ 138 | public function run($execLine) 139 | { 140 | $this->preRun(); 141 | $output = $this->_adapterClass->run($execLine); 142 | $this->postRun(); 143 | return $output; 144 | } 145 | 146 | /** 147 | * method to run before run is called 148 | */ 149 | public function preRun() 150 | { 151 | 152 | } 153 | 154 | /** 155 | * method to run after run is called 156 | */ 157 | public function postRun() 158 | { 159 | 160 | } 161 | 162 | /** 163 | * Appends a new log message to the log. 164 | * 165 | * @param string $severity 166 | * @param string $message 167 | */ 168 | public function log($severity, $message) 169 | { 170 | $this->_adapterClass->log($severity, $message); 171 | } 172 | 173 | /** 174 | * Calls refresh on the current entity -- refreshes the persistence state 175 | * 176 | */ 177 | public function refresh() 178 | { 179 | $this->_em->refresh($this->_jobEntity); 180 | } 181 | 182 | 183 | /** 184 | * @return int 185 | */ 186 | public function getId() 187 | { 188 | $this->refresh(); 189 | return $this->_jobEntity->getId(); 190 | } 191 | 192 | /** 193 | * @return string 194 | */ 195 | public function getName() 196 | { 197 | $this->refresh(); 198 | return $this->_jobEntity->getName(); 199 | } 200 | 201 | /** 202 | * @param string $name 203 | */ 204 | public function setName($name) 205 | { 206 | $this->_jobEntity->setName($name); 207 | $this->_em->persist($this->_jobEntity); 208 | $this->_em->flush(); 209 | } 210 | 211 | /** 212 | * @return int 213 | */ 214 | public function getRetry() 215 | { 216 | $this->refresh(); 217 | return $this->_jobEntity->getRetry(); 218 | } 219 | 220 | /** 221 | * @param int $retry 222 | */ 223 | public function setRetry($retry) 224 | { 225 | $this->_jobEntity->setRetry($retry); 226 | $this->_em->persist($this->_jobEntity); 227 | $this->_em->flush(); 228 | } 229 | 230 | /** 231 | * @return int 232 | */ 233 | public function getCooldown() 234 | { 235 | $this->refresh(); 236 | return $this->_jobEntity->getCooldown(); 237 | } 238 | 239 | /** 240 | * @param int $cooldown 241 | */ 242 | public function setCooldown($cooldown) 243 | { 244 | $this->_jobEntity->setCooldown($cooldown); 245 | $this->_em->persist($this->_jobEntity); 246 | $this->_em->flush(); 247 | } 248 | 249 | /** 250 | * @return int 251 | */ 252 | public function getMaxRetries() 253 | { 254 | $this->refresh(); 255 | return $this->_jobEntity->getMaxRetries(); 256 | } 257 | 258 | /** 259 | * @param int $maxRetries 260 | */ 261 | public function setMaxRetries($maxRetries) 262 | { 263 | $this->_jobEntity->setMaxRetries($maxRetries); 264 | $this->_em->persist($this->_jobEntity); 265 | $this->_em->flush(); 266 | } 267 | 268 | /** 269 | * @return int 270 | */ 271 | public function getAttempts() 272 | { 273 | $this->refresh(); 274 | return $this->_jobEntity->getAttempts(); 275 | } 276 | 277 | /** 278 | * @param int $attempts 279 | */ 280 | public function setAttempts($attempts) 281 | { 282 | $this->_jobEntity->setAttempts($attempts); 283 | $this->_em->persist($this->_jobEntity); 284 | $this->_em->flush(); 285 | } 286 | 287 | /** 288 | * @return string 289 | */ 290 | public function getExecutable() 291 | { 292 | $this->refresh(); 293 | return $this->_jobEntity->getExecutable(); 294 | } 295 | 296 | /** 297 | * @param string $executable 298 | */ 299 | public function setExecutable($executable) 300 | { 301 | $this->_jobEntity->setExecutable($executable); 302 | $this->_em->persist($this->_jobEntity); 303 | $this->_em->flush(); 304 | } 305 | 306 | /** 307 | * @return array 308 | */ 309 | public function getParams() 310 | { 311 | $params = array(); 312 | $this->refresh(); 313 | foreach($this->_jobEntity->getParams() as $param) { 314 | $params[$param->getKey()] = $param->getValue(); 315 | } 316 | 317 | return $params; 318 | } 319 | 320 | /** 321 | * @param array $params 322 | */ 323 | public function setParams(array $params) 324 | { 325 | //deactivate current associations 326 | foreach($this->_jobEntity->getParams() as $param) { 327 | $param->setActive(0); 328 | $this->_em->persist($param); 329 | $this->_em->flush(); 330 | unset($param); 331 | } 332 | 333 | //create new params 334 | foreach($params as $key => $value) { 335 | $param = new Param; 336 | $param->setKey($key); 337 | $param->setValue($value); 338 | $param->setJob($this->getId()); 339 | $param->setActive(1); 340 | $this->_em->persist($param); 341 | $this->_em->flush(); 342 | unset($param); 343 | } 344 | } 345 | 346 | /** 347 | * @return array 348 | */ 349 | public function getArgs() 350 | { 351 | $args = array(); 352 | $this->refresh(); 353 | foreach($this->_jobEntity->getArgs() as $arg) { 354 | array_push($args, $arg->getValue()); 355 | } 356 | 357 | return $args; 358 | } 359 | 360 | /** 361 | * @param array $args 362 | */ 363 | public function setArgs(array $args) 364 | { 365 | //deactivate current associations 366 | foreach($this->_jobEntity->getArgs() as $arg) { 367 | $arg->setActive(0); 368 | $this->_em->persist($arg); 369 | $this->_em->flush(); 370 | unset($arg); 371 | } 372 | 373 | //create new params 374 | foreach($args as $key => $value) { 375 | $arg = new Arg; 376 | $arg->setValue($value); 377 | $arg->setJob($this->getId()); 378 | $arg->setActive(1); 379 | $this->_em->persist($arg); 380 | $this->_em->flush(); 381 | unset($arg); 382 | } 383 | } 384 | 385 | /** 386 | * @return array 387 | */ 388 | public function getTags() 389 | { 390 | $tags = array(); 391 | $this->refresh(); 392 | foreach($this->_jobEntity->getTags() as $tag) { 393 | array_push($tags, $tag->getValue()); 394 | } 395 | return $tags; 396 | } 397 | 398 | /** 399 | * @param array $tags 400 | */ 401 | public function setTags(array $tags) 402 | { 403 | //deactivate current associations 404 | foreach($this->_jobEntity->getTags() as $tag) { 405 | $tag->setActive(0); 406 | $this->_em->persist($tag); 407 | $this->_em->flush(); 408 | unset($tag); 409 | } 410 | 411 | //create new params 412 | foreach($tags as $key => $value) { 413 | $tag = new Tag; 414 | $tag->setValue($value); 415 | $tag->setJob($this->getId()); 416 | $tag->setActive(1); 417 | $this->_em->persist($tag); 418 | $this->_em->flush(); 419 | unset($tag); 420 | } 421 | } 422 | 423 | /** 424 | * @return array 425 | */ 426 | public function getHistory() 427 | { 428 | $this->refresh(); 429 | $history = array(); 430 | $counter = 0; 431 | foreach($this->_jobEntity->getHistory() as $log) { 432 | $history[$counter] = array( 433 | 'timestamp' => $log->getTimestamp(), 434 | 'severity' => $log->getSeverity(), 435 | 'message' => $log->getMessage(), 436 | 'status' => $log->getStatus(), 437 | 'job' => $log->getJob(), 438 | 'id' => $log->getId(), 439 | ); 440 | $counter ++; 441 | } 442 | return $history; 443 | } 444 | 445 | /** 446 | * @param array $result 447 | */ 448 | public function addHistory(array $result) 449 | { 450 | //add a history entry 451 | $history = new History; 452 | $history->setJob($this->_jobEntity); 453 | $history->setTimestamp($this->getLastrunDate()); 454 | $history->setStatus($result['status']); 455 | $history->setSeverity($result['severity']); 456 | $history->setMessage($result['message']); 457 | $history->setActive(1); 458 | $this->_em->persist($history); 459 | $this->_em->flush(); 460 | unset($history); 461 | } 462 | 463 | /** 464 | * @return string 465 | */ 466 | public function getStatus() 467 | { 468 | $this->refresh(); 469 | return $this->_jobEntity->getStatus(); 470 | } 471 | 472 | /** 473 | * @param string $status 474 | */ 475 | public function setStatus($status) 476 | { 477 | $this->_jobEntity->setStatus($status); 478 | $this->_em->persist($this->_jobEntity); 479 | $this->_em->flush(); 480 | } 481 | 482 | /** 483 | * @return string 484 | */ 485 | public function getType() 486 | { 487 | $this->refresh(); 488 | return $this->_jobEntity->getType(); 489 | } 490 | 491 | /** 492 | * @param string $type 493 | */ 494 | public function setType($type) 495 | { 496 | $this->_jobEntity->setType($type); 497 | $this->_em->persist($this->_jobEntity); 498 | $this->_em->flush(); 499 | } 500 | 501 | /** 502 | * @return \DateTime 503 | */ 504 | public function getCreateDate() 505 | { 506 | $this->refresh(); 507 | return $this->_jobEntity->getCreateDate(); 508 | } 509 | 510 | /** 511 | * @param \DateTime $date 512 | */ 513 | public function setCreateDate(\DateTime $date) 514 | { 515 | $this->_jobEntity->setCreateDate($date); 516 | $this->_em->persist($this->_jobEntity); 517 | $this->_em->flush(); 518 | } 519 | 520 | /** 521 | * @return \DateTime 522 | */ 523 | public function getLastrunDate() 524 | { 525 | $this->refresh(); 526 | return $this->_jobEntity->getLastrunDate(); 527 | } 528 | 529 | /** 530 | * @param \DateTime $date 531 | */ 532 | public function setLastRunDate(\DateTime $date) 533 | { 534 | $this->_jobEntity->setLastRunDate($date); 535 | $this->_em->persist($this->_jobEntity); 536 | $this->_em->flush(); 537 | } 538 | 539 | /** 540 | * @return int 541 | */ 542 | public function getActive() 543 | { 544 | $this->refresh(); 545 | return $this->_jobEntity->getActive(); 546 | } 547 | 548 | /** 549 | * @param int $active 550 | */ 551 | public function setActive($active) 552 | { 553 | $this->_jobEntity->setActive($active); 554 | $this->_em->persist($this->_jobEntity); 555 | $this->_em->flush(); 556 | } 557 | 558 | /** 559 | * @return string 560 | */ 561 | public function getSchedule() 562 | { 563 | $this->refresh(); 564 | return $this->_jobEntity->getSchedule(); 565 | } 566 | 567 | /** 568 | * @param string $schedule 569 | */ 570 | public function setSchedule($schedule) 571 | { 572 | $this->_jobEntity->setSchedule($schedule); 573 | $this->_em->persist($this->_jobEntity); 574 | $this->_em->flush(); 575 | } 576 | 577 | /** 578 | * @return int 579 | */ 580 | public function getParent() 581 | { 582 | $this->refresh(); 583 | return $this->_jobEntity->getParent(); 584 | } 585 | 586 | /** 587 | * @param int $parent 588 | */ 589 | public function setParent($parent) 590 | { 591 | $this->_jobEntity->setParent($parent); 592 | $this->_em->persist($this->_jobEntity); 593 | $this->_em->flush(); 594 | } 595 | 596 | } 597 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 14 | 15 | 16 | ./Tests 17 | 18 | 19 | 20 | 21 | 22 | --------------------------------------------------------------------------------