ユーザーが入力した値を受け取り、アプリケーションのフォームでその入力を表すオブジェクト(フォームのデータを格納する入れ物、フォームDTO: Data Transfer Objectと名づけます)が組み立てられます。このフォームDTOの持つデータが妥当かどうかをチェックするのがバリデーションの役割です。
use JMS\DiExtraBundle\Annotation As DI;
/**
* @DI\Service("domain.member.allow_upgrade_spec")
*/
class AllowUpgradeSpecification
{
/**
* inject services if you need
*/
private $memberRepository;
public function isSatisfiedBy($member)
{
// リポジトリなどを使った条件のチェック
...
return true;
}
}
<?php
namespace PHPMentors\ValidatorBundle\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
/**
* @Annotation
*/
class ServiceCallback extends Constraint
{
public $method;
public $service;
public $message = 'Service callback returns an error.';
/**
* {@inheritdoc}
*/
public function getTargets()
{
return self::CLASS_CONSTRAINT;
}
/**
* {@inheritdoc}
*/
public function validatedBy()
{
return 'PHPMentorsServiceCallbackValidator';
}
}
<?php
namespace PHPMentors\ValidatorBundle\Validator\Constraints;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
class ServiceCallbackValidator extends ConstraintValidator implements ContainerAwareInterface
{
/**
* @var ContainerInterface
*/
protected $container;
/**
* {@inheritdoc}
*/
public function validate($object, Constraint $constraint)
{
if (null === $object) {
return;
}
if (!$this->container->has($constraint->service)) {
throw new ConstraintDefinitionException;
}
$service = $this->container->get($constraint->service);
if (!method_exists($service, $constraint->method)) {
throw new ConstraintDefinitionException(sprintf('Method "%s" targeted by ServiceCallback constraint does not exist', $constraint->method));
}
$result = call_user_func(array($service, $constraint->method), $object);
if (false == $result) {
$this->context->addViolation($constraint->message);
}
}
/**
* {@inheritdoc}
*/
public function setContainer(ContainerInterface $container = null)
{
$this->container = $container;
}
}
<?php
namespace PHPMentors\Sample\FormSampleBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
class AnswerType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->add('name')
->add('email')
->add('answerDetails', 'collection', array(
'type' => new AnswerDetailType(),
))
;
}
public function getName()
{
return 'answertype';
}
}
<?php
// snip
namespace Symfony\Tests\Component\HttpKernel;
use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Config\Loader\LoaderInterface;
class KernelTest extends \PHPUnit_Framework_TestCase
{
// snip
public function testBootInitializesBundlesAndContainer()
{
$kernel = $this->getMockBuilder('Symfony\Tests\Component\HttpKernel\KernelForTest')
->disableOriginalConstructor()
->setMethods(array('initializeBundles', 'initializeContainer', 'getBundles'))
->getMock();
$kernel->expects($this->once())
->method('initializeBundles');
$kernel->expects($this->once())
->method('initializeContainer');
$kernel->expects($this->once())
->method('getBundles')
->will($this->returnValue(array()));
$kernel->boot();
}
...
class AppKernel extends Kernel
{
public function registerBundles()
{
$bundles = array(
...
new PHPMentors\WorkflowerBundle\PHPMentorsWorkflowerBundle(),
);
...
...
use PHPMentors\DomainKata\Usecase\CommandUsecaseInterface;
use PHPMentors\Workflower\Process\Process;
use PHPMentors\Workflower\Process\ProcessAwareInterface;
use PHPMentors\Workflower\Process\WorkItemContextInterface;
use PHPMentors\Workflower\Workflow\Activity\ActivityInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
...
class LoanRequestProcessCompletionUsecase implements CommandUsecaseInterface, ProcessAwareInterface
{
...
/**
* @var Process
*/
private $process;
...
/**
* {@inheritdoc}
*/
public function setProcess(Process $process)
{
$this->process = $process;
}
...
/**
* {@inheritdoc}
*/
public function run(EntityInterface $entity)
{
assert($entity instanceof WorkItemContextInterface);
$this->process->completeWorkItem($entity);
...
ここまでの作業が終われば、Webインターフェイスやコマンドラインインターフェイス(Command Line Interface: CLI)からビジネスプロセスに対する一連の操作を実行できるようになります。
BPMSによるジェネレーティブプログラミングの実現に向けて
この記事では、Workflowerを使ったビジネスプロセスの管理をSymfonyアプリケーション上で行うために必要な作業について見てきました。WorkflowerおよびPHPMentorsWorkflowerBundleが提供するのはBPMN 2.0のワークフロー要素に対応するWorkflowドメインモデルと基本的なインテグレーションレイヤーに留まるため、実際にアプリケーションに組み込むためにはさらなる作業(とスキル)が要求されるでしょう。それは決して簡単なことではありません。なぜなら、それは対象ドメインに適したBPMS(Business Process Management System)あるいはBPMSフレームワークの設計に他ならないからです。
当時は C++ の他に、テレコム業界向け言語である CHILL や、軍需産業向け言語である Ada などの言語があり、それらの言語がオブジェクト指向なのかという議論がされていました。
最終的に C++ が残ることになりますが、C++ の作者である Stroustrup 氏は、そもそも C++ をオブジェクト指向言語ではなく、マルチパラダイム言語と定義していたことに注意する必要があります。
当時考えられていたオブジェクト指向の構成要素は次の3項目でした。
inheritance / 継承
polymorphism / ポリモーフィズム
instantiation / インスタンス化
1994年に Coplien さんは David Weiss 氏と Robert Chi Tao Lai 氏とともに仕事をしますが、その時、共通性/可変性分析を通じて、ソフトウェアファミリの振る舞いなどを特徴づけ、当時取り組んでいたソフトウェアのための DSL(Domain Specific Language) を開発しました。開発言語は C でした。