I use Laravel 8 with supervisor.
I have an error on a few jobs in my application.
All jobs run in about 3 seconds.
I have a 60 second timeout and a 90 second retry_after.
There are only a few jobs that fail per day out of about 3000 jobs. I have the jobs of a client which fails all the time I don't know why.
On some jobs I get the following error:
has been attempted too many times or run too long. The job may have previously timed out. {"exception":"[object] (Illuminate\Queue\MaxAttemptsExceededException(code: 0)
I think I have an error because of the middleware, when I remove it it works.
job.php
class UpdateTimeslots implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $tries = 3;
protected $officeId;
protected $startDate;
protected $endDate;
/**
* Create a new job instance.
*
* #return void
*/
public function __construct(int $officeId, string $startDate = null, string $endDate = null)
{
$this->onQueue('availabilities');
$this->officeId = $officeId;
$this->startDate = $startDate;
$this->endDate = $endDate;
}
/**
* Execute the job.
*
* #return void
*/
public function handle()
{
app(TimeslotMergedService::class)->update([$this->officeId], $this->startDate, $this->endDate);
}
/**
* Get the middleware the job should pass through.
*
* #return array
*/
public function middleware()
{
return [(new WithoutOverlapping($this->officeId))];
}
/**
* Handle a job failure.
*
* #param \Throwable $exception
* #return void
*/
public function failed(\Throwable $exception)
{
Log::error([
'office_id' => $this->officeId,
'start_date' => $this->startDate,
'end_date' => $this->endDate,
'error' => $exception->getMessage()
]);
}
}
queue.php
'database' => [
'driver' => 'database',
'table' => 'jobs',
'queue' => ['tasks', 'queue'],
'retry_after' => 90,
'after_commit' => false,
],
laravel-worker.conf
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/app/artisan queue:work --queue=tasks,default --tries=3 --max-time=3600
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=welrdv
numprocs=4
redirect_stderr=true
stdout_logfile=/var/www/app/storage/logs/worker.log
stopwaitsecs=3600
Can you help me please ?
UPDATE
here is the middleware code:
<?php
namespace Illuminate\Queue\Middleware;
use Illuminate\Container\Container;
use Illuminate\Contracts\Cache\Repository as Cache;
use Illuminate\Support\InteractsWithTime;
class WithoutOverlapping
{
use InteractsWithTime;
/**
* The job's unique key used for preventing overlaps.
*
* #var string
*/
public $key;
/**
* The number of seconds before a job should be available again if no lock was acquired.
*
* #var \DateTimeInterface|int|null
*/
public $releaseAfter;
/**
* The number of seconds before the lock should expire.
*
* #var int
*/
public $expiresAfter;
/**
* The prefix of the lock key.
*
* #var string
*/
public $prefix = 'laravel-queue-overlap:';
/**
* Create a new middleware instance.
*
* #param string $key
* #param \DateTimeInterface|int|null $releaseAfter
* #param \DateTimeInterface|int $expiresAfter
* #return void
*/
public function __construct($key = '', $releaseAfter = 0, $expiresAfter = 0)
{
$this->key = $key;
$this->releaseAfter = $releaseAfter;
$this->expiresAfter = $this->secondsUntil($expiresAfter);
}
/**
* Process the job.
*
* #param mixed $job
* #param callable $next
* #return mixed
*/
public function handle($job, $next)
{
$lock = Container::getInstance()->make(Cache::class)->lock(
$this->getLockKey($job), $this->expiresAfter
);
if ($lock->get()) {
try {
$next($job);
} finally {
$lock->release();
}
} elseif (! is_null($this->releaseAfter)) {
$job->release($this->releaseAfter);
}
}
/**
* Set the delay (in seconds) to release the job back to the queue.
*
* #param \DateTimeInterface|int $releaseAfter
* #return $this
*/
public function releaseAfter($releaseAfter)
{
$this->releaseAfter = $releaseAfter;
return $this;
}
/**
* Do not release the job back to the queue if no lock can be acquired.
*
* #return $this
*/
public function dontRelease()
{
$this->releaseAfter = null;
return $this;
}
/**
* Set the maximum number of seconds that can elapse before the lock is released.
*
* #param \DateTimeInterface|int $expiresAfter
* #return $this
*/
public function expireAfter($expiresAfter)
{
$this->expiresAfter = $this->secondsUntil($expiresAfter);
return $this;
}
/**
* Set the prefix of the lock key.
*
* #param string $prefix
* #return $this
*/
public function withPrefix(string $prefix)
{
$this->prefix = $prefix;
return $this;
}
/**
* Get the lock key for the given job.
*
* #param mixed $job
* #return string
*/
public function getLockKey($job)
{
return $this->prefix.get_class($job).':'.$this->key;
}
}
Related
I'm trying to send notification to user(i have only one user ,it's a dashboard)
in the App if the start_date is approaching ,so i tried to use task scheduler but i don't know how to use notification with task scheduler , should I create a table that save the notification in the database ?
So this is what i did:
class CongeNotification extends Notification
{
use Queueable;
public $conge;
/**
* Create a new notification instance.
*
* #return void
*/
public function __construct(Conge $conge)
{
$this->conge = $conge;
}
/**
* Get the notification's delivery channels.
*
* #param mixed $notifiable
* #return array
*/
public function via($notifiable)
{
return ['database'];
}
public function toArray($notifiable)
{
return [
'conge_id' => $this->conge->id
];
}
and this is my command:
class NotifyUsers extends Command
{
/**
* The name and signature of the console command.
*
* #var string
*/
protected $signature = 'users:notify';
/**
* The console command description.
*
* #var string
*/
protected $description = 'conge notification added';
/**
* Create a new command instance.
*
* #return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* #return int
*/
public function handle()
{
//$conge =Conge::all();
$now = Carbon::now();
$conge =Conge::where('start_date','<',$now->addDays(7))->get() ->each(function ($conge) {
DB::table('notification')->insert($conge->id);
});
}
`please any help`
i tried to create in handle a notification that notify the user a 7 day before the start_date
I have problem with limit email exchange from SMTP. I have counting table with specific column. total is 201. that total will send email automatic with schedule task on the server.
Counting TOTAL
can I send email per batch using laravel as much as 201 email in once send email?
cronEmail
/**
* The name and signature of the console command.
*
* #var string
*/
protected $signature = 'email:reminder';
/**
* The console command description.
*
* #var string
*/
protected $description = 'Command description';
/**
* Create a new command instance.
*
* #return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* #return mixed
*/
public function handle()
{
$this->updateMailConfig();
$check = DB::table('a_kpi')->join('d_mem','k_created_by','m_id')
->leftjoin('a_kpid','kd_kpi_id','k_id')
->where('k_status_id',1)
->where(function($query){
$query->where('kd_status_id','=','In Progress')
->orwhere('kd_status_id','=',null)
->orwhere('kd_status_id','=')
->orwhere('kd_status_id','=','null')
->orwhere('kd_status_id','=',"null");
})
->get();
$dt = date("Y-m-d");
$dtdt = date( "Y-m-d", strtotime( "$dt +10 day" ) );
for ($i=0; $i <count($check); $i++) {
if ($check[$i]->kd_duedate == $dtdt) {
$mail = $check[$i]->m_email;
Mail::send('mail.tes',
['pesan' => 'KPI INFORMATION',
'k_label' => $check[$i]->k_label,
'kd_tacticalstep' => $check[$i]->kd_tacticalstep,
'kd_duedate' => $check[$i]->kd_duedate],
function ($message) use ($mail)
{
$message->subject('REMINDER');
$message->to($mail);
});
$data = DB::table('d_log_reminder')
->insert([
'dlr_id'=>$check[$i]->k_id,
'dlr_kpi_id'=>$check[$i]->k_id,
'dlr_kpid_id'=>$check[$i]->kd_id,
'dlr_tacticalstep'=>$check[$i]->kd_tacticalstep,
'dlr_label'=>$check[$i]->k_label,
'dlr_duedate'=>$check[$i]->kd_duedate,
'dlr_created_by'=>$check[$i]->k_created_by,
'dlr_send_to'=>$mail
]);
}
}
// $check = DB::table('d_mem')->where('m_username','admin')->update(['m_code'=>'cor'.date('d-m-y h:i:s')]);
}
}
Kernel.php
<?php
namespace App\Console;
use Illuminate\Console\Scheduling\Schedule;
use App\Helper\ConfigUpdater;
use Mail;
use DB;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel
{
use ConfigUpdater;
/**
* The Artisan commands provided by your application.
*
* #var array
*/
protected $commands = [
'App\Console\Commands\cronEmail'
//
];
/**
* Define the application's command schedule.
*
* #param \Illuminate\Console\Scheduling\Schedule $schedule
* #return void
*/
protected function schedule(Schedule $schedule)
{
$schedule->command('email:reminder')
->everyMinute();
}
/**
* Register the Closure based commands for the application.
*
* #return void
*/
protected function commands()
{
require base_path('routes/console.php');
}
}
I created an observer to make some actions whenever a transaction is created / updated / deleted.
It works fine for the created / deleting method, but for some reason the updating and updated methods aren't being fired.
This is my observer:
<?php
namespace App\Observers\Finance;
use App\Finance\Account;
use App\Finance\Transaction;
use Illuminate\Contracts\Queue\ShouldQueue;
class TransactionObserver implements ShouldQueue
{
/**
* The name of the queue the job should be sent to.
*
* #var string|null
*/
public $queue = 'transactions';
/**
* Updates account balance
*
* #param Account $Account
* #param int $amount
* #param int $old_amount
*/
public function updateAccountBalance(Account $Account, $amount = 0, $old_amount = 0)
{
$newBalance = $Account->balance - ($old_amount * 100) + ($amount * 100);
$Account->update(['balance' => $newBalance]);
}
/**
* Listen to the Transaction created event.
*
* #param Transaction $Transaction
* #return void
*/
public function created(Transaction $Transaction)
{
$this->updateAccountBalance($Transaction->account, $Transaction->amount);
}
/**
* Listen to the Transaction updating event.
*
* #param Transaction $Transaction
* #return void
*/
public function updating(Transaction $Transaction)
{
$this->updateAccountBalance($Transaction->account, 0, $Transaction->amount);
}
/**
* Listen to the Transaction updated event.
*
* #param Transaction $Transaction
* #return void
*/
public function updated(Transaction $Transaction)
{
$this->updateAccountBalance($Transaction->account, $Transaction->amount);
}
/**
* Listen to the Transaction deleted event.
*
* #param Transaction $Transaction
* #return void
*/
public function deleting(Transaction $Transaction)
{
$this->updateAccountBalance($Transaction->account, 0, $Transaction->amount);
}
}
and the update method in the controller:
public function update(Request $request, Transaction $transaction)
{
if (in_array($transaction->id, Transaction::$nonEditable)) {
abort(404);
}
$this->validate($request, [
'title' => 'required|max:255',
'account_id' => 'required|exists:accounts,id',
'season_year' => 'required|integer|exists:seasons,year',
'finance_category_id' => 'required|integer|exists:finance_categories,id',
'amount' => 'numeric',
'date' => 'required|date_format:d/m/Y',
]);
$transaction->update($request->all());;
flash()->success('האובייקט עודכן בהצלחה');
return redirect()->route('transactions.index');
}
What am I doing wrong?
Plus, does ShouldQueue work with observers?
I have a problem creating a form by including a formType that are 2 related entities and that must be combined with a CollectionType.
I use the library jquery.collection for CollectionType ( http://symfony-collection.fuz.org/symfony3/ )
My entities
Product
namespace BBW\ProductBundle\Entity\Product;
use Doctrine\ORM\Mapping as ORM;
use Knp\DoctrineBehaviors\Model as ORMBehaviors;
/**
* Product
*
* #ORM\Table(name="product_product")
* #ORM\Entity(repositoryClass="BBW\ProductBundle\Repository\Product\ProductRepository")
*/
class Product
{
use ORMBehaviors\Translatable\Translatable;
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var array
* #ORM\ManyToMany(targetEntity="BBW\ProductBundle\Entity\Attribute\AttributeGroup", inversedBy="products", fetch="EAGER")
* #ORM\JoinTable(name="product_join_attribute_group")
*/
private $attributeGroups;
/**
* #var array
* #ORM\ManyToMany(targetEntity="BBW\ProductBundle\Entity\Attribute\Attribute", inversedBy="products", fetch="EAGER")
* #ORM\JoinTable(name="product_join_attribute")
*/
private $attributes;
/**
* Constructor
*/
public function __construct()
{
$this->attributeGroups = new \Doctrine\Common\Collections\ArrayCollection();
$this->attributes = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Add attributeGroup
*
* #param \BBW\ProductBundle\Entity\Attribute\AttributeGroup $attributeGroup
*
* #return Product
*/
public function addAttributeGroup(\BBW\ProductBundle\Entity\Attribute\AttributeGroup $attributeGroup)
{
$this->attributeGroups[] = $attributeGroup;
return $this;
}
/**
* Remove attributeGroup
*
* #param \BBW\ProductBundle\Entity\Attribute\AttributeGroup $attributeGroup
*/
public function removeAttributeGroup(\BBW\ProductBundle\Entity\Attribute\AttributeGroup $attributeGroup)
{
$this->attributeGroups->removeElement($attributeGroup);
}
/**
* Get attributeGroups
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getAttributeGroups()
{
return $this->attributeGroups;
}
/**
* Add attribute
*
* #param \BBW\ProductBundle\Entity\Attribute\Attribute $attribute
*
* #return Product
*/
public function addAttribute(\BBW\ProductBundle\Entity\Attribute\Attribute $attribute)
{
$this->attributes[] = $attribute;
return $this;
}
/**
* Remove attribute
*
* #param \BBW\ProductBundle\Entity\Attribute\Attribute $attribute
*/
public function removeAttribute(\BBW\ProductBundle\Entity\Attribute\Attribute $attribute)
{
$this->attributes->removeElement($attribute);
}
/**
* Get attributes
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getAttributes()
{
return $this->attributes;
}
}
AttributeGroup
namespace BBW\ProductBundle\Entity\Attribute;
use Doctrine\ORM\Mapping as ORM;
use Knp\DoctrineBehaviors\Model as ORMBehaviors;
/**
* AttributeGroup
*
* #ORM\Table(name="product_attribute_group")
* #ORM\Entity(repositoryClass="BBW\ProductBundle\Repository\Attribute\AttributeGroupRepository")
*/
class AttributeGroup
{
use ORMBehaviors\Translatable\Translatable;
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var int
*
* #ORM\OneToMany(targetEntity="BBW\ProductBundle\Entity\Attribute\Attribute", mappedBy="attributeGroup", cascade={"persist", "remove"}, fetch="EAGER")
* #ORM\JoinColumn(name="attribute_id")
*/
private $attributes ;
/**
* #var array
* #ORM\ManyToMany(targetEntity="BBW\ProductBundle\Entity\Product\Product", mappedBy="attributeGroups", cascade={"persist"}, fetch="EAGER")
*/
private $products;
/**
* Constructor
*/
public function __construct()
{
$this->attributes = new \Doctrine\Common\Collections\ArrayCollection();
$this->products = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Add attribute
*
* #param \BBW\ProductBundle\Entity\Attribute\Attribute $attribute
*
* #return AttributeGroup
*/
public function addAttribute(\BBW\ProductBundle\Entity\Attribute\Attribute $attribute)
{
$this->attributes[] = $attribute;
return $this;
}
/**
* Remove attribute
*
* #param \BBW\ProductBundle\Entity\Attribute\Attribute $attribute
*/
public function removeAttribute(\BBW\ProductBundle\Entity\Attribute\Attribute $attribute)
{
$this->attributes->removeElement($attribute);
}
/**
* Get attributes
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getAttributes()
{
return $this->attributes;
}
/**
* Add product
*
* #param \BBW\ProductBundle\Entity\Product\Product $product
*
* #return AttributeGroup
*/
public function addProduct(\BBW\ProductBundle\Entity\Product\Product $product)
{
$this->products[] = $product;
return $this;
}
/**
* Remove product
*
* #param \BBW\ProductBundle\Entity\Product\Product $product
*/
public function removeProduct(\BBW\ProductBundle\Entity\Product\Product $product)
{
$this->products->removeElement($product);
}
/**
* Get products
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getProducts()
{
return $this->products;
}
}
Attributes
namespace BBW\ProductBundle\Entity\Attribute;
use Doctrine\ORM\Mapping as ORM;
use Knp\DoctrineBehaviors\Model as ORMBehaviors;
/**
* Attribute
*
* #ORM\Table(name="product_attribute")
* #ORM\Entity(repositoryClass="BBW\ProductBundle\Repository\Attribute\AttributeRepository")
*/
class Attribute
{
use ORMBehaviors\Translatable\Translatable;
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var int
*
* #ORM\ManyToOne(targetEntity="BBW\ProductBundle\Entity\Attribute\AttributeGroup", inversedBy="attributes", cascade={"persist"}, fetch="EAGER")
* #ORM\JoinColumn(name="attribute_group_id")
*/
private $attributeGroup ;
/**
* #var array
* #ORM\ManyToMany(targetEntity="BBW\ProductBundle\Entity\Product\Product", mappedBy="attributes", cascade={"persist"}, fetch="EAGER")
*/
private $products;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set attributeGroup
*
* #param \BBW\ProductBundle\Entity\Attribute\AttributeGroup $attributeGroup
*
* #return Attribute
*/
public function setAttributeGroup(\BBW\ProductBundle\Entity\Attribute\AttributeGroup $attributeGroup = null)
{
$this->attributeGroup = $attributeGroup;
return $this;
}
/**
* Get attributeGroup
*
* #return \BBW\ProductBundle\Entity\Attribute\AttributeGroup
*/
public function getAttributeGroup()
{
return $this->attributeGroup;
}
/**
* Constructor
*/
public function __construct()
{
$this->products = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Add product
*
* #param \BBW\ProductBundle\Entity\Product\Product $product
*
* #return Attribute
*/
public function addProduct(\BBW\ProductBundle\Entity\Product\Product $product)
{
$this->products[] = $product;
return $this;
}
/**
* Remove product
*
* #param \BBW\ProductBundle\Entity\Product\Product $product
*/
public function removeProduct(\BBW\ProductBundle\Entity\Product\Product $product)
{
$this->products->removeElement($product);
}
/**
* Get products
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getProducts()
{
return $this->products;
}
}
Explanation
My product must belong to an attribute group and must contain attributes. I have a relationship between my AttributeGroup entity and a Relationship with Attribute.
My Entity AttributeGroup has a relationship with Attribute (functional).
Form Types
In my form I have a CollectionType with another formType as entry_type I have created with two entity types (AttributeGroups / Attributes)
FormType for product
namespace BBW\ProductBundle\Form\Product;
use A2lix\TranslationFormBundle\Form\Type\TranslationsType;
use BBW\CoreBundle\FormTypes\SwitchType;
use BBW\MediaBundle\Transformers\MediaTransformer;
use BBW\ProductBundle\Form\Attribute\Type\AttributeType;
use Doctrine\ORM\Mapping\Entity;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\Extension\Core\Type\MoneyType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ProductEditType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$locale = $options['int_service'];
$builder
->add('attributes', CollectionType::class, array(
'entry_type' => AttributeType::class,
'entry_options' => array(
'label' => false
),
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'required' => false,
'attr' => array(
'class' => 'attributes-selector'
),
))
;
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'BBW\ProductBundle\Entity\Product\Product',
'int_service' => ['fr'],
'translation_domain' => 'ProductBundle'
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'bbw_productbundle_edit_product';
}
}
AttributeType
/**
* Form Type: Fields add in product edit form
*/
namespace BBW\ProductBundle\Form\Attribute\Type;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class AttributeType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('attributeGroups', EntityType::class, array(
'label' => false,
'class' => 'BBW\ProductBundle\Entity\Attribute\AttributeGroup',
'choice_label' => 'translate.name',
'attr' => array(
'class' => 'choiceAttributeGroup'
),
))
->add('attributes', EntityType::class, array(
'label' => false,
'class' => 'BBW\ProductBundle\Entity\Attribute\Attribute',
'choice_label' => 'translate.name',
'attr' => array(
'class' => 'choiceAttributes'
)
))
;
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'BBW\ProductBundle\Entity\Product\Product',
'int_service' => ['fr'],
'translation_domain' => 'ProductBundle'
));
}
}
Explanation
The two entities (AttributeGroups / Attributes) are two linked drop-down lists. When I select a group, I must display the data of this group in the second drop-down list. This part works well. I can duplicate as much as I want (with the CollectionType).
First problem when i submit the form:
"Could not determine access type for property "attributeGroups" ".
Second problem when i set "mapped => false" in my two entities:
"Expected value of type "Doctrine\Common\Collections\Collection|array" for association field "BBW\ProductBundle\Entity\Product\Product#$attributes", got "BBW\ProductBundle\Entity\Product\Product" instead."
Screeshoots dump/form
Conclusion
I think my problem is a mapping problem, having tested a lot of code and explanation on the symfony doc or other site, I could not solve my problem. If anyone could help me out and explain the good work of a collectionType with 2 entity and if that is possible. If you have examples of codes I am taker too.
Thank you in advance for your help.
I'm trying to send a post request including two timestamps to my REST api.
The problem is that the timestamps are marked as invalid. "This value is not valid."
What am I doing wrong?
This is the request:
POST http://localhost:8000/app_test.php/api/projects/1/tasks/1/timetrackings
Accept: application/json
Content-Type: application/json
{"timeStart":1390757625,"timeEnd":1390757625,"projectMember":1}
The Controller looks as follows:
class MemberController extends BaseApiController implements ClassResourceInterface
{
public function postAction($projectId, $taskId, Request $request)
{
/** #var EntityManager $em */
$em = $this->getDoctrine()->getManager();
$this->findProject($em, $projectId);
$task = $this->findTask($em, $projectId, $taskId);
$request->request->add(array(
'task' => $taskId,
));
$form = $this->createForm(new TimeTrackType(), new TimeTrack());
$form->submit($request->request->all());
if ($form->isValid())
{
/** #var TimeTrack $tracking */
$tracking = $form->getData();
$task->addTimeTrack($tracking);
$em->flush();
return $this->redirectView(
$this->generateUrl('api_get_project_task_timetracking', array(
'projectId' => $projectId,
'taskId' => $taskId,
'trackingId' => $tracking->getId(),
)),
Codes::HTTP_CREATED
);
}
return View::create($form, Codes::HTTP_BAD_REQUEST);
}
}
The TimeTrackType class:
namespace PMTool\ApiBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class TimeTrackType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('timeStart', 'datetime', array(
'input' => 'timestamp',
))
->add('timeEnd', 'datetime', array(
'input' => 'timestamp',
))
->add('projectMember')
->add('task')
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'PMTool\ApiBundle\Entity\TimeTrack',
'csrf_protection' => false,
));
}
/**
* #return string
*/
public function getName()
{
return 'timetrack';
}
}
The entity class:
namespace PMTool\ApiBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use \DateTime;
/**
* TimeTrack
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="PMTool\ApiBundle\Entity\TimeTrackRepository")
*/
class TimeTrack
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var DateTime
*
* #ORM\Column(name="timeStart", type="datetime")
*/
private $timeStart;
/**
* #var DateTime
*
* #ORM\Column(name="timeEnd", type="datetime")
*/
private $timeEnd;
/**
* #var ProjectMember
*
* #ORM\ManyToOne(targetEntity="ProjectMember")
*/
private $projectMember;
/**
* #var Task
*
* #ORM\ManyToOne(targetEntity="Task", inversedBy="timeTracks")
* #ORM\JoinColumn(name="taskId", referencedColumnName="id")
*/
private $task;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set timeStart
*
* #param DateTime $timeStart
* #return TimeTrack
*/
public function setTimeStart($timeStart)
{
$this->timeStart = $timeStart;
return $this;
}
/**
* Get timeStart
*
* #return DateTime
*/
public function getTimeStart()
{
return $this->timeStart;
}
/**
* Set timeEnd
*
* #param DateTime $timeEnd
* #return TimeTrack
*/
public function setTimeEnd($timeEnd)
{
$this->timeEnd = $timeEnd;
return $this;
}
/**
* Get timeEnd
*
* #return DateTime
*/
public function getTimeEnd()
{
return $this->timeEnd;
}
/**
* #return \PMTool\ApiBundle\Entity\Task
*/
public function getTask()
{
return $this->task;
}
/**
* #param \PMTool\ApiBundle\Entity\Task $task
* #return $this
*/
public function setTask($task)
{
$this->task = $task;
return $this;
}
/**
* #return \PMTool\ApiBundle\Entity\ProjectMember
*/
public function getProjectMember()
{
return $this->projectMember;
}
/**
* #param \PMTool\ApiBundle\Entity\ProjectMember $projectMember
* #return $this
*/
public function setProjectMember($projectMember)
{
$this->projectMember = $projectMember;
return $this;
}
}
You can use a transformer to achieve this. (See Symfony Transformers)
Here is an Example of my FormType:
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToTimestampTransformer;
...
$transformer = new DateTimeToTimestampTransformer();
$builder->add($builder->create('validFrom', 'text')
->addModelTransformer($transformer)
)
Be sure to use "'text'" as an input type, otherwise it didn't work for me.
the input option is just for the model side of your underlying data object. In your case it should be datetime.
Your problem is, that you want to transform the timestamp into view data that the symfony form datetime form type does recognise. And I dont know how to do that, unfortunately.
http://symfony.com/doc/current/reference/forms/types/datetime.html#input