namespace
CleanPhp\Invoicer\Persistence\Hydrator;
use
CleanPhp\Invoicer\Domain\Repository\CustomerRepositoryInterface;
use
Zend\Stdlib\Hydrator\HydratorInterface;
class
OrderHydrator
implements
HydratorInterface {
// ...
public function
extract
(
$object
) {
return
$this
->
wrappedHydrator
->
extract
(
$object
);
}
// ...
}
If you want to write a spec for this, feel free. It’s simple enough that I’ll assume the Zend
developers have tested it enough for us and that we didn’t mess up such a simple call.
Viewing Orders
Let’s work on the view page for Orders now. First thing we’ll want to do is setup a route. We’ll
adapt our existing
/orders
route to optionally allow an
/action
and
/:id
to handle all this magic
in one.
Another option would be to add an explicit child route for
/view/:id
. Either way will result in
a 404 if a user navigates to a sub route that doesn’t exist, such as
/orders/steal-all-the-gold
.
Our Application in Zend Framework 2
160
// module/Application/config/module.config.php
return
[
// ...
'router'
=>
'routes'
=>
[
// ...
'orders'
=>
[
'type'
=>
'Segment'
,
'options'
=>
[
'route'
=>
'/orders[/:action[/:id]]'
,
'defaults'
=>
[
'controller'
=>
'Application\Controller\Orders'
,
'action'
=>
'index'
,
],
],
],
// ...
],
]
];
Now, we need a controller action to serve this route in our
OrdersController
:
public function
viewAction
() {
$id
=
$this
->
params
()
->
fromRoute
(
'id'
);
$order
=
$this
->
orderRepository
->
getById
(
$id
);
return
[
'order'
=>
$order
];
}
The
viewAction()
is pretty simple: we grab the ID from the route params, query for that Order
from the
$orderRepository
that was injected into the controller, and return it to the view.
Finally, we’ll need a view file to display our Order data:
Our Application in Zend Framework 2
161
module
/
Application
/
views
/
application
/
orders
/
view
.
phtml
-->
<
div class
=
"page-header clearfix"
>
<
h2
>
Order
#= $this->escapeHtml($this->order->getOrderNumber()) ?>
div
>
<
table class
=
"table table-striped"
>
<
thead
>
<
tr
>
<
th colspan
=
"2"
>
Order Details
th
>
tr
>
thead
>
<
tr
>
<
th
>
Customer
:
th
>
<
td
>
<
a href
=
"/customers/edit/=
$this->escapeHtmlAttr
(
$this->order
->getCustomer()->getId()) ?>"
>
=
$this
->
escapeHtml
(
$this
->
order
->
getCustomer
()
->
getName
())
?>
Description: |
=
$this
->
escapeHtml
(
$this
->
order
->
getDescription
())
?>
|
Total: |
$
=
number_format
(
$this
->
order
->
getTotal
(),
2
)
?>
|
This simple view is just using a table to dump out details about our Order, and provides a link
back to the Customer record. It’s super simple. But what happens when we navigate to an ID
that doesn’t exist in the database? We’ll get a giant error.
Let’s handle this case by throwing a 404:
$order
=
$this
->
orderRepository
->
getById
(
$id
);
if
(
!
$order
) {
$this
->
getResponse
()
->
setStatusCode
(
404
);
return null
;
}
If we don’t get an
Order
object back (and instead get
null
), we simply grab the response stored
on the object, set it’s status to 404, and return (to halt further processing).
We can check this in the browser by navigation to an Order ID that doesn’t exist.
Our Application in Zend Framework 2
162
Creating Orders
Creating an Order will be very similar to creating a Customer.
We’ll start by writing a spec for our
OrderFilter
, which we’ll use to validate our form data:
// specs/input-filter/order.spec.php
use
CleanPhp\Invoicer\Service\InputFilter\OrderInputFilter;
describe(
'InputFilter\Order'
,
function
() {
beforeEach(
function
() {
$this
->
inputFilter
=
new
OrderInputFilter();
});
describe(
'->isValid()'
,
function
() {
// ...
});
});
We’re simply setting up an instance of our new
OrderInputFilter
so it’s available to specs.
We’ll have to test each form element within the
->isValid()
block, so let’s start by testing the
customer_id
:
it(
'should require a customer.id'
,
function
() {
$isValid
=
$this
->
inputFilter
->
isValid
();
$error
=
[
'id'
=>
[
'isEmpty'
=>
'Value is required and can\'t be empty'
]
];
$customer
=
$this
->
inputFilter
->
getMessages
()[
'customer'
];
expect(
$isValid
)
->
to
->
equal
(
false
);
expect(
$customer
)
->
to
->
equal
(
$error
);
});
For
customer_id
, we’re just interested in making sure it was provided. In the future, we could,
and should, also validate that the provided value is actually a customer in the database.
When we build our form, instead of using the database value (
customer_id
), we’re going to use
our entity relationships, which means that we’ll be looking for
customer[id]
via the POST data,
so our input filter is going to treat
customer
as an array.
Our Application in Zend Framework 2
163
We’re testing here using the logic of the
Required
validator in Zend framework. Obviously, if
we ever switch our validation library, we’ll have to update the specs to the format they provide.
On to
orderNumber
:
it(
'should require an order number'
,
function
() {
$isValid
=
$this
->
inputFilter
->
isValid
();
$error
=
[
'isEmpty'
=>
'Value is required and can\'t be empty'
];
$orderNo
=
$this
->
inputFilter
->
getMessages
()[
'orderNumber'
];
expect(
$isValid
)
->
to
->
equal
(
false
);
expect(
$orderNo
)
->
to
->
equal
(
$error
);
});
Here, we’re simply validating that the
orderNumber
value was provided. Again, we’re using the
language of the domain,
orderNumber
, instead of the database column of
order_number
.
We also want to make sure it falls within our constraints of being exactly 13 characters in length:
it(
'should require order numbers be 13 chars long'
,
function
() {
$scenarios
=
[
[
'value'
=>
'124'
,
'errors'
=>
[
'stringLengthTooShort'
=>
'The input is less than 13 characters long'
]
],
[
'value'
=>
'20001020-0123XR'
,
'errors'
=>
[
'stringLengthTooLong'
=>
'The input is more than 13 characters long'
]
],
[
'value'
=>
'20040717-1841'
,
'errors'
=>
null
]
];
foreach
(
$scenarios
as
$scenario
) {
Our Application in Zend Framework 2
164
$this
->
inputFilter
=
new
OrderInputFilter();
$this
->
inputFilter
->
setData
([
'orderNumber'
=>
$scenario
[
'value'
]
])
->
isValid
();
$messages
=
$this
->
inputFilter
->
getMessages
()[
'orderNumber'
];
expect(
$messages
)
->
to
->
equal
(
$scenario
[
'errors'
]);
}
});
The
$scenarios
variable lists several different scenarios to test, and which errors we would
expect in each scenario.
Next, we’ll test the
description
:
it(
'should require a description'
,
function
() {
$isValid
=
$this
->
inputFilter
->
isValid
();
$error
=
[
'isEmpty'
=>
'Value is required and can\'t be empty'
];
$messages
=
$this
->
inputFilter
->
getMessages
()[
'description'
];
expect(
$isValid
)
->
to
->
equal
(
false
);
expect(
$messages
)
->
to
->
equal
(
$error
);
});
Finally, we’ll test the total, and ensure that it’s a floating point number value:
it(
'should require a total'
,
function
() {
$isValid
=
$this
->
inputFilter
->
isValid
();
$error
=
[
'isEmpty'
=>
'Value is required and can\'t be empty'
];
$messages
=
$this
->
inputFilter
->
getMessages
()[
'total'
];
expect(
$isValid
)
->
to
->
equal
(
false
);
expect(
$messages
)
->
to
->
equal
(
$error
);
});
Our Application in Zend Framework 2
165
it(
'should require total to be a float value'
,
function
() {
$scenarios
=
[
[
'value'
=> 124
,
'errors'
=>
null
],
[
'value'
=>
'asdf'
,
'errors'
=>
[
'notFloat'
=>
'The input does not appear to be a float'
]
],
[
'value'
=> 99.99
,
'errors'
=>
null
]
];
foreach
(
$scenarios
as
$scenario
) {
$this
->
inputFilter
=
new
OrderInputFilter();
$this
->
inputFilter
->
setData
([
'total'
=>
$scenario
[
'value'
]
])
->
isValid
();
$messages
=
$this
->
inputFilter
->
getMessages
()[
'total'
];
expect(
$messages
)
->
to
->
equal
(
$scenario
[
'errors'
]);
}
});
We provide a list of scenarios again, and check each one of them to make sure we get the expected
error messages, or no error messages in the case of valid input.
Now that we’ve defined our spec, let’s go ahead and write the Input Filter:
Our Application in Zend Framework 2
166
// src/Service/InputFilter/OrderInputFilter.php
Do'stlaringiz bilan baham: |