CustomersController->newAction()
Let’s start building out our
newAction()
and the corresponding view file:
// module/Application/src/Application/Controllers/CustomersController.php
namespace
Application\Controller;
use
CleanPhp\Invoicer\Domain\Repository\CustomerRepositoryInterface;
use
Zend\Mvc\Controller\AbstractActionController;
class
CustomersController
extends
AbstractActionController {
// ...
public function
newAction
() {
}
}
This simple controller action is all we initially need. Let’s build out our view:
Our Application in Zend Framework 2
137
module
/
Application
/
view
/
application
/
customers
/
new
.
phtml
-->
<
div class
=
"page-header clearfix"
>
<
h2
>
New
Customer
h2
>
div
>
<
form role
=
"form"
action
=
""
method
=
"post"
>
<
div class
=
"form-group"
>
<
label
for
=
"name"
>
Name
:
label
>
<
input type
=
"text"
class
=
"form-control"
name
=
"name"
id
=
"name"
placeholder
=
"Enter Name"
>
div
>
<
div class
=
"form-group"
>
<
label
for
=
"email"
>
Email
:
label
>
<
input type
=
"text"
class
=
"form-control"
name
=
"email"
id
=
"email"
placeholder
=
"Enter Email"
>
div
>
<
button type
=
"submit"
class
=
"btn btn-primary"
>
Save
button
>
form
>
Next, we’ll need to update our routing to handle
/customers/new
:
// module/Application/config/module.config.php
return
[
'router'
=>
[
'routes'
=>
[
// ...
'customers'
=>
[
// ...
'may_terminate'
=>
true
,
'child_routes'
=>
[
'create'
=>
[
'type'
=>
'Segment'
,
'options'
=>
[
'route'
=>
'/new'
,
'defaults'
=>
[
'action'
=>
'new'
,
],
]
],
]
],
// ...
],
],
Our Application in Zend Framework 2
138
// ...
];
This simple child route will combine
/customers
of the parent route, with
/new
of the child
route to give us our
/customers/new
route, which will point to the
newAction()
of our
CustomersController()
.
Now if we click on the Create Customer link, we should see our new form rendered. Now we
just have to make this form do something.
CustomerInputFilter
We’re going to use Zend’s
InputFilter
to validate and sanitize our input. You can read more
about Zend’s Input Filters in
their documentation⁴²
, but essentially, they give us a set of classes
to validate and sanitize input data.
We’re going to drop our InputFilters into the
src/
directory, as we’ll want to use them when
we decide to switch away from Zend Framework. Otherwise, we’d have to build a whole new
solution for validating input data, which would be fine, but it’s nice not to have to do that at the
same time.
We’ll start by writing a spec to describe the behavior we want. First, we’ll need an instance of
our soon-to-be new
CustomerInputFilter
for testing:
// specs/input-filter/customer.spec.php
use
CleanPhp\Invoicer\Service\InputFilter\CustomerInputFilter;
describe(
'InputFilter\Customer'
,
function
() {
beforeEach(
function
() {
$this
->
inputFilter
=
new
CustomerInputFilter();
});
describe(
'->isValid()'
,
function
() {
// ...
});
});
We’ll be interested in testing the
isValid()
method, which Zend provides to determine whether
an
InputFilter
’s data is valid. We’ll also use the
setData()
method to supply the
InputFilter
with some data to test.
Let’s start with testing validity of the customer name:
⁴²
http://framework.zend.com/manual/current/en/modules/zend.input-filter.intro.html
Our Application in Zend Framework 2
139
it(
'should require a name'
,
function
() {
$isValid
=
$this
->
inputFilter
->
isValid
();
$error
=
[
'isEmpty'
=>
'Value is required and can\'t be empty'
];
$messages
=
$this
->
inputFilter
->
getMessages
()[
'name'
];
expect(
$isValid
)
->
to
->
equal
(
false
);
expect(
$messages
)
->
to
->
equal
(
$error
);
});
Last, we’ll test the validity of the email address. Here, we’re not particularly worried about the
exact messages ZF2 returns when we have invalid data, just that we get some kind of array of
errors back, rather than
null
:
it(
'should require an email'
,
function
() {
$isValid
=
$this
->
inputFilter
->
isValid
();
$error
=
[
'isEmpty'
=>
'Value is required and can\'t be empty'
];
$messages
=
$this
->
inputFilter
->
getMessages
()[
'email'
];
expect(
$isValid
)
->
to
->
equal
(
false
);
expect(
$messages
)
->
to
->
equal
(
$error
);
});
it(
'should require a valid email'
,
function
() {
$scenarios
=
[
[
'value'
=>
'bob'
,
'errors'
=>
[]
],
[
'value'
=>
'bob@bob'
,
'errors'
=>
[]
],
[
'value'
=>
'bob@bob.com'
,
'errors'
=>
null
]
Our Application in Zend Framework 2
140
];
foreach
(
$scenarios
as
$scenario
) {
$this
->
inputFilter
->
setData
([
'email'
=>
$scenario
[
'value'
]
])
->
isValid
();
$messages
=
$this
->
inputFilter
->
getMessages
()[
'email'
];
if
(
is_array
(
$messages
)) {
expect(
$messages
)
->
to
->
be
->
a
(
'array'
);
expect(
$messages
)
->
to
->
not
->
be
->
empty
();
}
else
{
expect(
$messages
)
->
to
->
be
->
null
();
}
}
});
We can add some more robust data to the list of tested
$scenarios
if we want to more
fully test the email RFC for valid emails, but we can also trust that ZF2 handles all the
cases pretty well. We just want to make sure that our
CustomerInputFilter
is setting
up the validation rules correctly.
Now let’s write a new
InputFilter
class for Customer data:
// src/Service/InputFilter/CustomerInputFilter.php
namespace
CleanPhp\Invoicer\Service\InputFilter;
use
Zend\InputFilter\Input;
use
Zend\InputFilter\InputFilter;
use
Zend\Validator\EmailAddress;
class
CustomerInputFilter
extends
InputFilter {
public function
__construct
() {
$name
=
(
new
Input(
'name'
))
->
setRequired
(
true
);
$email
=
(
new
Input(
'email'
))
->
setRequired
(
true
);
$email
->
getValidatorChain
()
->
attach
(
new
EmailAddress()
);
Our Application in Zend Framework 2
141
$this
->
add
(
$name
);
$this
->
add
(
$email
);
}
}
Posting Customer Data
Our next step is to utilize this
CustomerInputFilter
in our
CustomersController
. We’ll want
to do this when we receive a POST request only, and if we receive validation errors, we should
report those back to the user. Let’s start by writing a spec of our intended behavior.
First, we’ll need to inject an instance of the
CustomerInputFilter
into the
CustomersController
as part of the test:
// module/Application/src/Application/Controller/CustomersController.php
namespace
Application\Controller;
use
CleanPhp\Invoicer\Domain\Repository\CustomerRepositoryInterface;
use
CleanPhp\Invoicer\Service\InputFilter\CustomerInputFilter;
use
Zend\Mvc\Controller\AbstractActionController;
class
CustomersController
extends
AbstractActionController {
protected
$customerRepository
;
protected
$inputFilter
;
public function
__construct
(
CustomerRepositoryInterface
$customers
,
CustomerInputFilter
$inputFilter
) {
$this
->
customerRepository
=
$customers
;
$this
->
inputFilter
=
$inputFilter
;
}
// ...
}
Now we can update the
newAction()
to handle a POST request:
Our Application in Zend Framework 2
142
// module/Application/src/Application/Controller/CustomersController.php
namespace
Application\Controller;
use
CleanPhp\Invoicer\Domain\Repository\CustomerRepositoryInterface;
use
CleanPhp\Invoicer\Service\InputFilter\CustomerInputFilter;
use
Zend\Mvc\Controller\AbstractActionController;
class
CustomersController
extends
AbstractActionController {
// ...
public function
newAction
() {
if
(
$this
->
getRequest
()
->
isPost
()) {
$this
->
inputFilter
->
setData
(
$this
->
params
()
->
fromPost
());
if
(
$this
->
inputFilter
->
isValid
()) {
}
else
{
}
}
}
}
First, we determine if the request is a
POST
request. If it is, we supply our InputFilter with the
posted form data, then check to see if the InputFilter is valid, given that data.
We have two remaining paths to implement:
1. The data is valid
2. The data is invalid
When the data is valid, we want to persist it to our repository. However, the data coming in from
the POST is a giant array. We need to be able to persist an instance of
Customer
. The best way to
handle this is to hydrate a
Customer
object with the POST data. To do that, we’ll need to inject
an instance of a
HydratorInterface
into the controller:
// module/Application/src/Application/Controller/CustomersController.php
namespace
Application\Controller;
use
CleanPhp\Invoicer\Domain\Repository\CustomerRepositoryInterface;
use
CleanPhp\Invoicer\Service\InputFilter\CustomerInputFilter;
use
Zend\Mvc\Controller\AbstractActionController;
use
Zend\StdLib\Hydrator\HydratorInterface;
class
CustomersController
extends
AbstractActionController {
Our Application in Zend Framework 2
143
protected
$customerRepository
;
protected
$inputFilter
;
public function
__construct
(
CustomerRepositoryInterface
$customers
,
CustomerInputFilter
$inputFilter
,
HydratorInterface
$hydrator
) {
$this
->
customerRepository
=
$customers
;
$this
->
inputFilter
=
$inputFilter
;
$this
->
hydrator
=
$hydrator
;
}
// ...
}
We’ll also want to update our controller config to inject these two new objects that the
CustomersController
needs:
// module/Application/config/module.config.php
use
CleanPhp\Invoicer\Service\InputFilter\CustomerInputFilter;
use
Zend\Stdlib\Hydrator\ClassMethods;
return
[
// ...
'controllers'
=>
[
'invokables'
=>
[
'Application\Controller\Index'
=>
'Application\Controller\IndexController'
],
'factories'
=>
[
'Application\Controller\Customers'
=>
function
(
$sm
) {
return new
\Application\Controller\CustomersController(
$sm
->
getServiceLocator
()
->
get
(
'CustomerTable'
),
new
CustomerInputFilter(),
new
ClassMethods()
);
},
],
],
// ...
];
Next, we’re going to use the hydrator to build a
Customer
object, and then persist that customer
object using our
CustomerRepository
:
Our Application in Zend Framework 2
144
public function
newAction
() {
if
(
$this
->
getRequest
()
->
isPost
()) {
$this
->
inputFilter
->
setData
(
$this
->
params
()
->
fromPost
());
if
(
$this
->
inputFilter
->
isValid
()) {
$customer
=
$this
->
hydrator
->
hydrate
(
$this
->
inputFilter
->
getValues
(),
new
Customer()
);
$this
->
customerRepository
->
begin
()
->
persist
(
$customer
)
->
commit
();
}
else
{
}
}
}
We’ll also need a
use
statement for the
Customer
class at the top of the file.
At this point, we can enter a new Customer in the browser and have it persisted to the database.
But afterward, the user is dumped back to the New Customer page with no indication that their
save was successful.
Let’s add a redirect to the
/customers
page, as well as a flash message alerting them that the
save was successful:
public function
newAction
() {
if
(
$this
->
getRequest
()
->
isPost
()) {
$this
->
inputFilter
->
setData
(
$this
->
params
()
->
fromPost
());
if
(
$this
->
inputFilter
->
isValid
()) {
// ...
$this
->
flashMessenger
()
->
addSuccessMessage
(
'Customer Saved'
);
$this
->
redirect
()
->
toUrl
(
'/customers'
);
}
else
{
}
}
}
If you give it a shot in the browser, you should now be redirected to the
/customers
page. In order
to get the flash message to show up, we’ll need to setup our
layout.phtml
file to render flash
messages. Zend provides a
helper⁴³
to easily display these flash messages, but it looks terrible.
We’ll create our own partial file to render them, and then include that in our
layout.phtml
file.
⁴³
http://framework.zend.com/manual/current/en/modules/zend.view.helpers.flash-messenger.html
Our Application in Zend Framework 2
145
view
/
application
/
Do'stlaringiz bilan baham: |