Phpunit test a method using a service - php

I'm trying to test a method which is using a service, and apparently it's not possible to test it like a normal method.
Does someone know what to do ?
I have this code for the moment :
namespace PlatformBundle\Tests;
use PlatformBundle\Controller\PaymentController;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class PaymentControllerTest extends WebTestCase
{
private $payment;
public function __construct() { parent::__construct(); $this->payment = new PaymentController(); }
public function testSendEmail()
{
$param = array(
'info' => array(
'email' => 'test#test.com', 'name' => 'test', 'fare' => 'test', 'id' => 'test'
)
);
$this->assertEquals(true, $this->invokeMethod($this->payment, 'sendEmail', $param));
}
/**
* Call protected/private method of a class.
*
* #param object &$object Instantiated object that we will run method on.
* #param string $methodName Method name to call
* #param array $parameters Array of parameters to pass into method.
*
* #return mixed Method return.
*/
public function invokeMethod(&$object, $methodName, array $parameters = array())
{
$reflection = new \ReflectionClass(get_class($object));
$method = $reflection->getMethod($methodName);
$method->setAccessible(true);
return $method->invokeArgs($object, $parameters);
}
}
The controller where the method sendEmail is :
<?php
namespace PlatformBundle\Controller;
use PlatformBundle\Entity\Customer;
use PlatformBundle\Entity\Promocode;
use PlatformBundle\Entity\Transfer;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Config\Definition\Exception\Exception;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
class PaymentController extends Controller
{
public function checkoutAction(Request $req)
{
if (! $req->isMethod('POST')) throw new AccessDeniedHttpException();
$info = $req->request->all();
$this->container->get('platform.formSecurity')->testAllInformation($info);
$this->saveCustomerIntoDb($info);
$info['payed'] = false;
$session = $req->getSession();
$session->set('info', $info);
$info['date'] = $this->container->get('platform.useful')->reverseDateFormat($info['date']);
return $this->render('PlatformBundle:Payment:checkout.html.twig', array(
'isIndex' => false,
'info' => $info,
'stripe' => $this->stripeConfig()
));
}
public function cancelAction(Request $req)
{
$req->getSession()->invalidate();
return $this->render('PlatformBundle:Payment:cancel.html.twig', array('isIndex' => false));
}
public function successAction(Request $req)
{
$session = $req->getSession();
$info = $session->get('info');
if ($info['payed']) {
$req->getSession()->invalidate();
if ($info === null) throw new Exception('Please contact us to make sure that the payment has been done and that your order has been taken into account.');
$this->saveTransferIntoDb($info);
$customer = $this->getDoctrine()->getManager()->getRepository('PlatformBundle:Customer')->findOneBy(array(
'email' => $info['email']
));
$transfer = $this->getDoctrine()->getManager()->getRepository('PlatformBundle:Transfer')->findOneBy(
array('customer' => $customer->getId()),
array('id' => 'desc'),
1
);
$info['id'] = $transfer->getId();
$info['date'] = $this->container->get('platform.useful')->reverseDateFormat($info['date']);
$this->sendEmail($info);
// if 5 payments done, send a promocode
if (is_int($customer->getPayments() / 5)) {
$this->createAndSendNewPromocode($customer);
}
return $this->render('PlatformBundle:Payment:success.html.twig', array(
'isIndex' => false,
'info' => $info
));
} else return new RedirectResponse('cancel');
}
private function sendEmail($info)
{
$mail = $this->container->get('platform.mail');
$mail->send(
$info['email'],
'You have ordered a transfer for Dublin',
$this->renderView('PlatformBundle:Mail:orderSucceed.html.twig', array('info' => $info)),
'info#dubair.ie'
);
$mail->send(
'info#airportcollections.net, info#dubair.ie, info#365onlineholidays.com',
'A customer ordered a transfer for Dublin',
$this->renderView('PlatformBundle:Mail:report.html.twig', array('info' => $info)),
'info#dubair.ie'
);
}
private function saveCustomerIntoDb($info)
{
// test if the customer already exist
$customersList = $this->getDoctrine()->getManager()->getRepository('PlatformBundle:Customer')
->findByEmail($info['email']);
$customerExists = (sizeof($customersList) == 1 ? true : false);
if ($customerExists) {
$customer = $customersList[0];
} else {
// Create the entity
$customer = new Customer();
// dateRegistration, country and ip are automatically created in the constructor
$customer->setEmail($info['email']);
$customer->setPayments(0);
}
$customer->setName($info['name']);
$customer->setPhone($info['phone']);
$em = $this->getDoctrine()->getManager();
$em->persist($customer);
$em->flush();
}
private function saveTransferIntoDb($info)
{
$customers = $this->getDoctrine()->getManager()->getRepository('PlatformBundle:Customer')
->findByEmail($info['email']);
$customer = $customers[0];
$customer->setPayments($customer->getPayments() + 1);
// make promocode outdated
if ($info['promocode'] != '') {
$promocode = $this->getDoctrine()->getManager()->getRepository('PlatformBundle:Promocode')
->findOneBy(array(
'value' => $info['promocode'],
'outdated' => 0,
'type' => 'short'
));
$promocode->setOutdated(1);
}
// test if transfer already exist
$transferList = $this->getDoctrine()->getManager()->getRepository('PlatformBundle:Transfer')->findBy(
array(
'customer' => $customer,
'pickup' => $info['pickup'],
'destination' => $info['destination'],
'pickupTime' => $info['pickupTime'],
'address' => $info['address']
), // criteria
array('pickup' => 'desc'), // sorting
5, // Limit
0 // Offset
);
// if transfer doesn't already exist, create it
if (sizeof($transferList) == 0) {
$transfer = new Transfer();
$transfer->setPickup($info['pickup']);
$transfer->setDestination($info['destination']);
$dateArray = explode('-', $info['date']);
$transfer->setDate(new \DateTime($dateArray[2].'-'.$dateArray[1].'-'.$dateArray[0]));
$transfer->setAddress($info['address']);
$transfer->setFlightTime($info['flightTime']);
$transfer->setPickupTime($info['pickupTime']);
$transfer->setSeats($info['seats']);
$transfer->setAirline($info['airline']);
$transfer->setFlight($info['flight']);
$transfer->setType($info['type']);
$transfer->setBags($info['bags']);
$transfer->setFare($info['fare']);
// join
$transfer->setCustomer($customer);
$em = $this->getDoctrine()->getManager();
$em->persist($transfer);
$em->flush();
}
}
private function createAndSendNewPromocode($customer)
{
$newPromocode = $this->container->get('platform.useful')->createRandomPassword();
$promocode = new Promocode();
$promocode->setValue($newPromocode);
$promocode->setType('short');
$promocode->setDiscount(10);
$em = $this->getDoctrine()->getManager();
$em->persist($promocode);
$em->flush();
$mail = $this->container->get('platform.mail');
$mail->send(
$customer->getEmail(),
'A promotional code for your next transfer on dubair.ie !',
$this->renderView('PlatformBundle:Mail:promocode.html.twig', array(
'customer' => $customer,
'promocode' => $newPromocode
)),
'info#dubair.ie'
);
}
private function stripeConfig()
{
$stripe = array(
"secret_key" => "xx",
"publishable_key" => "xx"
);
\Stripe\Stripe::setApiKey($stripe['secret_key']);
return $stripe;
}
public function stripeChargeAction(Request $req)
{
$this->stripeConfig();
$info = $req->getSession()->get('info');
$amount = ($info['fare'] * 100);
$info['payed'] = true;
$req->getSession()->set('info', $info);
$token = $req->request->get('stripeToken');
$customer = \Stripe\Customer::create(array(
'email' => $req->request->get('email'),
'card' => $token
));
$charge = \Stripe\Charge::create(array(
'customer' => $customer->id,
'amount' => $amount,
'currency' => 'eur'
));
return new RedirectResponse('success');
}
}
thanks

Related

How to add wishlist data to trailData without overwriting existing data?

I'm new to PHP and I'm building API for wishlist, for both guest users and logged-in users in Laravel Lumen. Here I'm using TrailManagerService as session manager
It saves data as
- for logged in users
Array
(
[trail_id] => 4b19bd9d-f2da-431b-8aba-d181d7eca736
[inception_time] => 1599813465
[last_used] => 1600762156
[customer_id] => 106210
[customer_data.customer_id] => 106210
[customer_data.firstname] => XXXX
[customer_data.lastname] => YYYY
[customer_data.gender] => Male
[customer_data.dob] => 1999-10-19
[customer_data.email] => xx#yy.com
[customer_data.mobile] => 2245436547
[customer_data.referral_code] => HRI11489
-for guest users
Array
(
[trail_id] => 8b7e6931-6ad3-48a0-af61-4caaab85cf20
[inception_time] => 1600761357
[last_used] => 1600761391
I want to add wishlist items to trailData and save it to DB if user does login and then emptytraildata after it get saved to DB. Also, if user doesnt login wishlist data should be saved in trailData for current session.
Code for WishlistControllerService
<?php
namespace App\Http\Services\v1\Products;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Redis;
use Illuminate\Http\Request;
use App\Services\SOA\TrailManagerService;
use App\Repositories\WishlistRepository;
class WishlistControllerService
{
public function __construct(WishlistRepository $wishlistRepository)
{
$this->wishlistRepository = $wishlistRepository;
}
public function showList(Request $request){
$trailData = TrailManagerService::getAllTrailData('customer_id');
$trailCustomerId = TrailManagerService::getTrailData('customer_id');
$headerCustomerId = (int) $request->header('X-Customer-ID', 0);
$responseData = [];
$trailCustomerId = $trailData['customer_id'] ?? 0;
print_r($trailData);exit;
if($trailCustomerId === $headerCustomerId) {
// print_r("hi");exit;
$wishlistData = $this->wishlistRepository->getWishlist($trailCustomerId);
$responseData = [
'identifier' => 'wishlist',
'data' => [
'list' => $wishlistData
]
];
} else if($trailCustomerId === 0) {
// print_r("bye");exit;
// $tempWishlistItem = [
// // 'guestUserID' => $,
// 'productID' => $request->input('product_id'),
// 'sizeID' => $trailData['size_id'],
// 'productQuantity' => $request->input('quantity'),
// 'shippingPin' => $request->input('postal_code'),
// 'shippingCity' => $request->input('city_name'),
// ];
$responseData = [
'identifier' => '',
'data' => [
'list' => []
]
];
} else {
// print_r("why");exit;
$responseData = [
'status' => 403,
'message' => 'Access Forbidden'
];
}
return $responseData;
}
public function store($request, $productId){
$trailData = TrailManagerService::getAllTrailData();
$responseData = [];
$headerCustomerId = (int) $request->header('X-Customer-ID', 0);
$trailCustomerId = $trailData['customer_id'] ?? 0;
$tempWishlistItem =
[
// 'siteUserID`' => $customerId ?? $trailCustomerId,
'productID' => $productId,
'sizeID' => $request->input('size_id'),
'productQuantity' => $request->input('quantity'),
'currency' => strtoupper($request->input('currencyCode')),
];
$tempData[] =
array_push($trailData, $tempWishlistItem);
TrailManagerService::setTrailData($trailData);
print_r($trailData);exit;
// if($trailCustomerId === $customerId) {
$wId = $this->wishlistRepository->create($wishlistModelData);
$responseData = ['data' => ['userWID' => $wId]];
$wishlistModelData = [
'siteUserID' => $customerId,
'productID' => $productId,
'sizeID' => $request->input('size_id'),
'productQuantity' => $request->input('quantity'),
'currency' => strtoupper($request->input('currencyCode')),
];
// print_r($wishlistModelData);exit;
$wishlistItem = $this->wishlistRepository
->findWhere(['status' => 1, 'siteUserID' => $customerId, 'productID' =>
$productId]);
if(empty($wishlistItem[0]) === false) {
$responseData = [
'status' => 403,
'message' => 'Forbidden, Item already in Wishlist'
];
} else {
$wId = $this->wishlistRepository->create($wishlistModelData);
$responseData = ['data' => ['userWID' => $wId]];
}
} else {
// $tempWishlistData = [];
$tempWishlistItem =
[
// 'siteUserID' => $customerId ?? $trailCustomerId,
'productID' => $productId,
'sizeID' => $request->input('size_id'),
'productQuantity' => $request->input('quantity'),
'currency' => strtoupper($request->input('currencyCode')),
];
$tempWishlistData = array_merge($trailData, [$tempWishlistItem]);
print_r($tempWishlistData);exit;
$trailDataTemp = TrailManagerService::setTrailData(
$tempWishlistData
);
print_r($trailDataTemp);exit;
if($customerId === $trailData['customerID']){
foreach($tempWishlistData as $item) {
$wId = $this->wishlistRepository->create($wishlistModelData);
$responseData = ['data' => ['userWID' => $wId]];
}
TrailManagerService::emptyTrailData();
}
return $responseData;
}
I want to add wishlist items to trailData and save it to DB if user does login and then emptytraildata after it get saved to DB. Also, if user doesnt login wishlist data should be saved in trailData for current session.
Code for Wishlist Controller
<?php
namespace App\Http\Controllers\v1\Products;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Http\Services\v1\Products\WishlistControllerService;
use Validator;
use App\Helpers\Utilities;
use App\Services\SOA\TrailManagerService;
use Illuminate\Support\Facades\Route;
class WishlistController extends Controller
{
private $controllerService;
public function __construct(WishlistControllerService $controllerService)
{
// $request = app(Request::class);
// $customerId = $request->header('X-Customer-ID', 0);
// TrailManagerService::authorize($customerId);
$this->controllerService = $controllerService;
}
public function index(Request $request)
{
$responseData = $this->controllerService->showList($request);
$httpStatus = $responseData['status'] ?? 200;
return $this->response($responseData, $httpStatus);
}
public function store(Request $request, int $productId){
$requestParams = $request->all();
// $requestParams['productId'] = $request->header('product_id');
$validator = Validator::make($requestParams,
[
'product_id' => 'integer',
'size_id' => 'integer',
'quantity' => 'required|integer|digits_between:1,2',
'currencyCode' => 'required|string|in:INR,EUR,USD,AUD,CAD,SGD,HKD'
],
[
'product_id.integer'=> 'Parameter product_id is mandatory',
'size_id.integer' => 'Invalid value for parameter size_id',
],
);
if ($validator->fails()) {
$messages = $validator->errors();
$responseData = Utilities::requestValiationResponse($messages);
} else {
// print_r($productId);exit;
$responseData = $this->controllerService->store($request, $productId);
}
$httpStatus = $responseData['status'] ?? 200;
return $this->response($responseData, $httpStatus);
}
public function remove(Request $request, int $productId)
{
$responseData = $this->controllerService->remove($request, $productId);
$httpStatus = $responseData['status'] ?? 200;
return $this->response($responseData, $httpStatus);
}
}
Code for Wishlist Repo
<?php
namespace App\Repositories;
use Prettus\Repository\Eloquent\BaseRepository;
use App\Models\MxUserWishlist;
use Illuminate\Support\Facades\DB;
use App\Helpers\Utilities;
class WishlistRepository extends BaseRepository
{
/**
* Specify Model class name
*
* #return string
*/
public function model()
{
return MxUserWishlist::class;
}
/**
* #param int $customerId
* #return array
*/
public function getWishlist($trailCustomerId): array
{
$wishlistData = [];
$query = "SELECT W.userWID, W.siteUserID, W.productID,P.productTitle, PSI.imageName,
W.sizeID, S.sizeTitle, D.designerName,P.designerID, P.categoryID,
PS.productPrice, PS.discountPercent,PS.filterPrice,P.seoUri
FROM mx_user_wishlist W
INNER JOIN mx_product P ON P.productID = W.productID
INNER JOIN mx_product_set PS ON (W.productID = PS.productID AND W.sizeID = PS.sizeID)
INNER JOIN mx_product_set_images PSI ON PSI.productID = W.productID
INNER JOIN mx_size S ON S.sizeID = W.sizeID
INNER JOIN mx_designer D ON P.designerID = D.designerID
WHERE W.siteUserID = $trailCustomerId";
$wishlistCollection = DB::select($query, ['siteUserID' => $trailCustomerId]);
if(empty($wishlistCollection) === false) {
foreach ($wishlistCollection as $key => $wishlist) {
$productId = 0;
$productUrl = '';
$productUrl = '/products/'.$wishlist->seoUri.'/'.$wishlist->productID;
$wishlistItems = [
'id' => $wishlist->productID,
'name' => $wishlist->productTitle,
'image' => config('global.cdni_url').'/tr:w-317/uploads/product/'.$wishlist->imageName,
'product_url' => $productUrl,
'sizes' => [
'id' => $wishlist->productID,
'name' => $wishlist->sizeTitle
],
'designer_name' => $wishlist->designerName,
'category_id' => $wishlist->categoryID,
'mrp' => $wishlist->productPrice,
'discount_percentage' => $wishlist->discountPercent,
'you_pay' => $wishlist->filterPrice,
];
}
return $wishlistItems;
} else {
$responseData = [];
return $responseData;
}
// return $responseData;
}

Call to member function hello() on null zendframework 3

I'm getting error
Call to a member function buildCTCCompensationData() on null
in PayrollspendController.php on this line of code:
$compData = $this->payrollspendManager->hello($postData);
I have checked the module.config.php and __construct() method but not able to find the error. What is the issue in the code?
Please help me to resolve me the error
I have called the method properly
if any code is required will help
if want view file that will also give
Those who know zendframework help me to resolve issue
This is module.config.php
'dashboard_activity_payrollspend' => [
'type' => Literal::class,
'options' => [
'route' => '/dashboard/employer-details/activity-manage',
'constraints' => [
'action' =>'[a-zA-Z][a-zA-Z0-9_-]*',
'id' => '[a-zA-Z0-9_-]+',
],
'defaults' => [
'controller' => Controller\PayrollspendController::class,
'action' => 'add',
],
],
],
'controllers' => [
'factories' => [
Controller\PayrollspendController::class => Controller\Factory\PayrollspendControllerFactory::class
],
],
'service_manager' => [
'factories' => [
Service\PayrollspendManager::class => Service\Factory\PayrollspendManagerFactory::class
],
],
This is my controller PayrollspendController.php
<?php
namespace Dashboard\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Zend\Session\Container;
use Application\Entity\PyPayGroup;
use Application\Entity\PyPayPeriod;
//use Payroll\Form\SalaryVariationForm;
use Zend\Session\SessionManager;
use Application\Entity\CommonCompanyHeader;
use Dashboard\Form\PayrollspendForm;
class PayrollspendController extends AbstractActionController
{
private $entityManager;
private $sessionContainer;
private $pyPayPeriodClass;
private $pyPayGroupClass;
private $companyClass;
public function __construct($entityManager)
{
$this->entityManager = $entityManager;
$this->pyPayGroupClass = $this->entityManager->getRepository(PyPayGroup::class);
$this->pyPayPeriodClass = $this->entityManager->getRepository(PyPayPeriod::class);
$this->companyClass = $this->entityManager->getRepository(CommonCompanyHeader::class);
$this->commonTranslationManager = $commonTranslationManager;
$sessionManager = new SessionManager();
$this->sessionContainer = new Container('ContainerNamespace', $sessionManager);
$arrLabelId = [];
}
public function addAction()
{
if ($this->sessionContainer->empId == "") {
return $this->redirect()->toRoute('admin_user_login');
}
if (!in_array('PY', $this->sessionContainer->arrRole)) {
if (!in_array('py_admin', $this->sessionContainer->arrRole)) {
return $this->redirect()->toRoute('dashboard_ess_index');
}
}
$reportForm = new PayrollspendForm();
$payGroup = $this->pyPayGroupClass->findBy([
'ouCode' => $this->sessionContainer->ouCode,
'langCode' => $this->sessionContainer->langCode,
'pgActive' => 1
]);
$reportForm->buildPayGroupData($payGroup);
$company = $this->companyClass->findBy([
'ouCode' => $this->sessionContainer->ouCode,
'langCode' => $this->sessionContainer->langCode
]);
$reportForm->buildCompanyData($company);
$payPeriodData = ['' => 'Select'];
$reportForm->get('payPeriod')->setValueOptions($payPeriodData);
$postData = $this->getRequest()->getPost()->toArray();
$postData['ouCode'] = $this->sessionContainer->ouCode;
$postData['langCode'] = $this->sessionContainer->langCode;
$compData = $this->payrollspendManager->hello($postData);
$groupByData = [
'' => 'Select',
'location' => 'Location',
'department' => 'Department',
'cost-center' => 'Cost center'
];
$reportForm->get('groupby')->setValueOptions($groupByData);
return new ViewModel([
'reportData' => $compData,
'form' => $reportForm,
'ouCode' => $this->sessionContainer->ouCode,
'postData' => $postData,
'langCode' => $this->sessionContainer->langCode,
'arrLabels' => $this->arrLabels
]);
$resultData->setTerminal(true);
return $resultData;
}
}
This is PayrollspendControllerFactory.php
<?php
namespace Dashboard\Controller\Factory;
use Interop\Container\ContainerInterface;
use Zend\ServiceManager\Factory\FactoryInterface;
use Dashboard\Service\PayrollspendManager;
//use Application\Service\CommonTranslationManager;
use Dashboard\Controller\PayrollspendController;
class PayrollspendControllerFactory implements FactoryInterface
{
public function __invoke(ContainerInterface $container,$requestedName, array $options = null)
{
$entityManager = $container->get('doctrine.entitymanager.orm_default');
$payrollspendManager = $container->get(PayrollspendManager::class);
//$hrManager = $container->get(HrManager::class);
// $commonTranslationManager = $container->get(CommonTranslationManager::class);
return new PayrollspendController($entityManager);
}
}
This PayrollspendManager.php
<?php
namespace Dashboard\Service;
//use Application\Entity\DsAnnouncement;
//use Application\Entity\TmDomain;
// The AnnouncementManager service is responsible for adding new task.
class PayrollspendManager
{
/**
* Doctrine entity manager.
* #var Doctrine\ORM\EntityManager
*/
private $entityManager;
// Constructor is used to inject dependencies into the service.
public function __construct($entityManager)
{
$this->entityManager = $entityManager;
}
public function hello($postData)
{
$headerData = $this->getAllCTCCompensationHeader($postData);
$compData = $this->getAllCTCCompensationData($postData);
return [
'header' => $headerData,
'detail' => $compData
];
}
public function getAllCTCCompensationHeader($postData)
{
$queryBuilder = $this->entityManager->createQueryBuilder();
$queryBuilder->select('DISTINCT(pc.pyComPayitem) as payHeader')
->from(PyCompensationDetails::class, 'pc')
->innerJoin(PyPayItemPaygroupMap::class, 'ppipm', 'with', 'ppipm.payitemCode = pc.pyComPayitem
AND ppipm.ouCode = pc.ouCode
AND ppipm.langCode= pc.langCode
AND ppipm.pgCode= pc.pgCode')
->where('pc.ouCode = ?1')
->andWhere('pc.langCode = ?2')
->andWhere('pc.pgCode = ?3')
->andWhere('pc.isModified = ?4')
->setParameter('1', $postData['ouCode'])
->setParameter('2', $postData['langCode'])
->setParameter('3', $postData['payGroup'])
->setParameter('4', 0)
->orderBy('ppipm.orderofProcessing', 'ASC');
$compData = $queryBuilder->getQuery()->getResult();
$headerData = [
'0' => 'Employee Id',
'1' => 'Employee Name'
];
foreach ($compData as $compHeader) {
$headerData[] = $compHeader['payHeader'];
}
$headerData[] = 'PF';
$headerData[] = 'ESIC';
$headerData[] = 'Total';
return $headerData;
}
/**
* Get compensation detail data
*
* #param type $postData
* #return type
*/
public function getAllCTCCompensationData($postData)
{
$queryBuilder = $this->entityManager->createQueryBuilder();
$queryBuilder->select('pc.pyComEmpid, pc.amount, pc.pyComPayitem, pi.payitemDesc, hn.empFname, hn.empMname, hn.empLname')
->from(PyCompensationDetails::class, 'pc')
->innerJoin(HrEmpid::class, 'he', 'with', 'pc.ouCode = he.ouCode
and pc.langCode = he.langCode
and pc.pyComEmpid = he.empId'
)
->innerJoin(HrEmpName::class, 'hn', 'with', 'pc.ouCode = hn.ouCode
and pc.langCode = hn.langCode
and pc.pyComEmpid = hn.empId'
)
->innerJoin(PyPayItem::class, 'pi', 'with', 'pc.ouCode = pi.ouCode
and pc.langCode = pi.langCode
and pc.pyComPayitem = pi.payitemCode'
)
//->innerJoin(SmartlistData::class,'sd','with','sd.dataCode = pi.smartlistPayitemtype'
//)
->leftJoin(PyPayItemPaygroupMap::class, 'ppipm', 'with', 'ppipm.payitemCode = pi.payitemCode '
. 'AND ppipm.ouCode = pi.ouCode '
. 'AND ppipm.langCode= pi.langCode '
. 'AND ppipm.pgCode= pc.pgCode')
->where('pc.ouCode = ?1')
->andWhere('pc.langCode = ?2')
->andWhere('pc.pgCode = ?3')
->andWhere('pc.isModified = ?4')
->andWhere('he.smartlistEmpstatus != ?5')
->andWhere('pi.smartlistPayitemtype IN (94,99,100)')
->orderBy('ppipm.orderofProcessing', 'ASC')
->setParameter('1', $postData['ouCode'])
->setParameter('2', $postData['langCode'])
->setParameter('3', $postData['payGroup'])
->setParameter('4', 0)
->setParameter('5', 56);
// ->orderBy('pc.pyComEmpid');
// echo $queryBuilder->getQuery()->getSQL();
//exit;
$compData = $queryBuilder->getQuery()->getResult();
// echo '<pre>';
//print_r($compData);
// exit;
$data = [];
if (!empty($compData)) {
$total = 0;
foreach ($compData as $dataC) {
$data[$dataC['pyComEmpid']]['Employee Id'] = $dataC['pyComEmpid'];
$data[$dataC['pyComEmpid']]['Employee Name'] = sprintf('%s %s %s', $dataC['empFname'], $dataC['empMname'], $dataC['empLname']);
$data[$dataC['pyComEmpid']][$dataC['pyComPayitem']] = $dataC['amount'];
$statData = $this->getStatuoryData($postData, $dataC['pyComEmpid']);
//echo '<pre>';
// print_r($statData);
//exit;
if(isset($statData['pf']) && ($statData['pf'] == 1)){
$parameterData = $this->getParamaterData($postData, 'pf', $dataC['pyComEmpid']);
$data[$dataC['pyComEmpid']]['PF'] = $this->getPFData($postData, $parameterData);
} else {
$data[$dataC['pyComEmpid']]['PF'] = 0;
}
if(isset($statData['esic']) && ($statData['esic'] == 1)){
$parameterData = $this->getParamaterData($postData, 'esic', $dataC['pyComEmpid']);
$data[$dataC['pyComEmpid']]['ESIC'] = $this->getESICData($postData, $dataC['pyComEmpid']);
} else {
$data[$dataC['pyComEmpid']]['ESIC'] = 0;
}
$data[$dataC['pyComEmpid']]['Total'] = $this->getCTCCompensationSum($postData, $dataC['pyComEmpid'], $data[$dataC['pyComEmpid']]['PF'], $data[$dataC['pyComEmpid']]['ESIC']);
}
}
//echo "<pre>";
//print_r($data);
//exit;
return $data;
}
/**
* Get compensation sum
*
* #param array $postData
* #param string $pyComEmpid
* #return type
*/
public function getCTCCompensationSum($postData, $pyComEmpid, $pf, $esic)
{
$amountTotal = 0;
$queryBuilder = $this->entityManager->createQueryBuilder();
$queryBuilder->select('SUM(pc.amount) as amount')
->from(PyCompensationDetails::class, 'pc')
->where('pc.ouCode = ?1')
->andWhere('pc.langCode = ?2')
->andWhere('pc.pgCode = ?3')
->andWhere('pc.pyComEmpid = ?4')
->andWhere('pc.isModified = ?5')
->setParameter('1', $postData['ouCode'])
->setParameter('2', $postData['langCode'])
->setParameter('3', $postData['payGroup'])
->setParameter('4', $pyComEmpid)
->setParameter('5', 0);
$compData = $queryBuilder->getQuery()->getOneOrNullResult();
$amount = isset($compData['amount']) ? $compData['amount'] : 0;
$amountTotal = $amount + $pf + $esic;
return $amountTotal;
}
}
This is PayrollspendManagerFactory.php
<?php
namespace Dashboard\Service\Factory;
use Interop\Container\ContainerInterface;
use Zend\ServiceManager\Factory\FactoryInterface;
use Dashboard\Service\PayrollspendManager;
class PayrollspendManagerFactory implements FactoryInterface
{
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
$entityManager = $container->get('doctrine.entitymanager.orm_default');
// Instantiate the service and inject dependencies
return new PayrollspendManager($entityManager);
}
}
This is PayrollspendForm
<?php
namespace Dashboard\Form;
use Zend\Form\Form;
use Zend\InputFilter\InputFilter;
//use Application\Entity\TmTask;
/**
* This form is used to collect post data.
*/
class PayrollspendForm extends Form
{
public $session;
public $entityManager;
public $ouCode;
public $langCode;
public function __construct()
{
// Define form name
parent::__construct('payrollspend-form');
// Set POST method for this form
$this->setAttribute('method', 'post');
$this->setAttribute('class', 'form-horizontal');
$this->setAttribute('id', 'payrollspend-form');
//$this->edit = $edit;
// $this->ouCode = $session->ouCode;
// $this->langCode = $session->langCode;
// $this->entityManager = $entityManager;
$this->addElements();
$this->addInputFilter();
}
protected function addElements()
{
$this->add([
'type' => 'select',
'name' => 'payPeriod',
'attributes' => [
'id' => 'payPeriod',
'class'=>'form-control'
]
]);
$this->add([
'type' => 'text',
'name' => 'payCalender',
'attributes' => [
'id' => 'payCalender',
'class'=>'form-control',
'disabled' => 'disabled'
]
]);
$this->add([
'type' => 'select',
'name' => 'companyCode',
'attributes' => [
'id' => 'companyCode',
'class'=>'form-control'
]
]);
$this->add([
'type' => 'select',
'name' => 'payGroup',
'attributes' => [
'id' => 'payGroup',
'class'=>'form-control'
]
]);
$this->add([
'type' => 'text',
'name' => 'startDate',
'attributes' => [
'id' => 'startDate',
'class' => 'form-control dpd1',
'data-date-format' => "dd-mm-yyyy"
]
]);
$this->add([
'type' => 'text',
'name' => 'endDate',
'attributes' => [
'id' => 'endDate',
'class' => 'form-control dpd1',
'data-date-format' => "dd-mm-yyyy"
]
]);
$this->add([
'type' => 'select',
'name' => 'groupby',
'attributes' => [
'id' => 'groupby',
'class'=>'form-control',
'placeholder'=>''
],
]);
$this->add([
'type' => 'submit',
'name' => 'submit',
'attributes' => [
'value' => 'Submit',
'id' => 'submitbutton',
'class' => 'btn btn-primary'
],
]);
}
private function addInputFilter()
{
$inputFilter = new InputFilter();
$this->setInputFilter($inputFilter);
}
public function buildPayGroupData($payGroup)
{
$payGroupData = [];
foreach($payGroup as $data){
$payGroupData[$data->pgCode] = $data->pgSdesc;
}
$this->get('payGroup')->setEmptyOption('select')->setValueOptions($payGroupData);
}
public function buildCompanyData($companys)
{
$companyData = ['' => 'Select'];
foreach($companys as $data){
$companyData[$data->companyCode] = $data->companyName;
}
$this->get('companyCode')->setValueOptions($companyData);
}
public function defaultDate()
{
$this->get('startDate')->setValue(date('d-m-Y'));
}
}
Assuming the call in PayrollspendController::addAction() is the problem.
$compData = $this->payrollspendManager->hello($postData);
This is because the $this->payrollspendManager variable has not been defined.
You can see in the PayrollspendControllerFactory::__invoke you have requested the service from the dependency injection container; but do not inject it into the constructor of the PayrollspendController.
// PayrollspendControllerFactory::__invoke()
$entityManager = $container->get('doctrine.entitymanager.orm_default');
$payrollspendManager = $container->get(PayrollspendManager::class);
return new PayrollspendController($entityManager);
You should update the factory :
return new PayrollspendController($entityManager, $payrollspendManager);
And also the constructor of the controller to allow the new argument (while you are there you can also type hint on the expected interface)
use \Doctrine\ORM\EntityManager;
use \Dashboard\Service\PayrollspendManager;
class PayrollspendController extends AbstractActionController
{
// ...
private $entityManager;
private $payrollspendManager;
public function __construct(
EntityManager $entityManager,
PayrollspendManager $payrollspendManager
){
$this->entityManager = $entityManager;
$this->payrollspendManager = $payrollspendManager;
}
// ...
}

Route parameter only works for one code Zend Framework 2

I'm trying to have a verification process after registration (by a randomly generated verification code), but after I verify one code, it will not verify another one even though I am using the code that is stored in the database upon registration. For instance:
verify/c42557235936ed755d3305e2f7305aa3
...works fine, but when I try and use another code (like /verify/3bc056ff48fec352702652cfa4850ac4), it generates the default layout for the application and does nothing. I don't know what is causing it.
Here is the code I have for this:
VerifyController -
namespace Application\Controller;
use Zend\Mvc\Controller\AbstractActionController;
class VerifyController extends AbstractActionController
{
public $verify;
public function indexAction()
{
$code = $this->params()->fromRoute('code');
if ($this->getVerifyInstance()->authenticateCode($code) !== false) {
$this->flashMessenger()->addSuccessMessage("Verification Successful, you can now login.");
return $this->redirect()->toRoute('verify', array('action' => 'success'));
} else {
$this->flashMessenger()->addErrorMessage("Oops! Something went wrong while attempting to verify your account, please try again.");
return $this->redirect()->toRoute('verify', array('action' => 'failure'));
}
}
public function successAction()
{
}
public function failureAction()
{
}
public function getVerifyInstance()
{
if (!$this->verify) {
$sm = $this->getServiceLocator();
$this->verify = $sm->get('Application\Model\VerifyModel');
}
return $this->verify;
}
}
VerifyModel -
namespace Application\Model;
use Zend\Db\TableGateway\TableGateway;
use Zend\Db\Sql\Sql;
use Zend\Db\Sql\Insert;
use Zend\Db\Adapter\Adapter;
class VerifyModel
{
/**
* #var TableGateway
*/
protected $table_gateway;
/**
* #var mixed
*/
protected $code;
/**
* Constructor method for VerifyModel class
* #param TableGateway $gateway
*/
public function __construct(TableGateway $gateway)
{
// check if $gateway was passed an instance of TableGateway
// if so, assign $this->table_gateway the value of $gateway
// if not, make it null
$gateway instanceof TableGateway ? $this->table_gateway = $gateway : $this->table_gateway = null;
}
public function authenticateCode($code)
{
// authenticate the verification code in the url against the one in the pending_users table
$this->code = !empty($code) ? $code : null;
$select = $this->table_gateway->select(array('pending_code' => $this->code));
$row = $select->current();
if (!$row) {
throw new \RuntimeException(sprintf('Invalid registration code %s', $this->code));
} else {
// verification code was found
// proceed to remove the user from the pending_users table
// and insert into the members table
$data = array(
'username' => $row['username'],
'password' => $row['password'],
);
$sql = new Sql($this->table_gateway->getAdapter());
$adapter = $this->table_gateway->getAdapter();
$insert = new Insert('members');
$insert->columns(array(
'username',
'password'
))->values(array(
'username' => $data['username'],
'password' => $data['password'],
));
$execute = $adapter->query(
$sql->buildSqlString($insert),
Adapter::QUERY_MODE_EXECUTE
);
if (count($execute) > 0) {
// remove the entry now
$delete = $this->table_gateway->delete(array('pending_code' => $this->code));
if ($delete > 0) {
return true;
}
}
}
}
}
the route:
'verify' => array(
'type' => 'Segment',
'options' => array(
'route' => 'verify/:code',
'constraints' => array(
'code' => '[a-zA-Z][a-zA-Z0-9_-]*',
),
'defaults' => array(
'controller' => 'Application\Controller\Verify',
'action' => 'index',
),
),
),
and the layout configurer in Module.php:
public function init(ModuleManager $manager)
{
$events = $manager->getEventManager();
$shared_events = $events->getSharedManager();
$shared_events->attach(__NAMESPACE__, 'dispatch', function ($e) {
$controller = $e->getTarget();
if (get_class($controller) == 'Application\Controller\SetupController') {
$controller->layout('layout/setup');
} else if (get_class($controller) == 'Application\Controller\MemberLoginController' || get_class($controller) == 'Application\Controller\AdminLoginController') {
$controller->layout('layout/login');
} else if (get_class($controller) == 'Application\Controller\RegisterController') {
$controller->layout('layout/register');
} else if (get_class($controller) == 'Application\Controller\VerifyController') {
$controller->layout('layout/verify');
}
}, 100);
}
Your route is defined
'options' => array(
'route' => 'verify/:code',
'constraints' => array(
'code' => '[a-zA-Z][a-zA-Z0-9_-]*',
),
So, it should start with a letter (upper or lower case), and be followed by any (even none) number of characters (letters, numbers, underscores, and dashes).
So, valid routes:
verify/c42557235936ed755d3305e2f7305aa3 (the one you where trying)
verify/abcde
verify/N123-123
verify/Z
verify/X-1
etc.
Any of those should work. But the other code you provide in your question:
/verify/3bc056ff48fec352702652cfa4850ac4
starts with a number, so it wont be caught by your router. You need to either change how you generate your codes so they match your route, or change your route so it matches your codes. E.g.:
'options' => array(
'route' => 'verify/:code',
'constraints' => array(
'code' => '[a-zA-Z0-9][a-zA-Z0-9_-]{28,32}',
),

Seeding database - [ErrorException] Trying to get property of non-object

I am using "zizaco/confide": "~4.0#dev" and "zizaco/entrust": "1.2.*#dev".
I have set everything up as described in the two tutorials(confide migrations). Furthermore, I have created the following models:
User:
<?php
use Zizaco\Confide\ConfideUser;
use Zizaco\Confide\Confide;
use Zizaco\Confide\ConfideEloquentRepository;
use Zizaco\Entrust\HasRole;
use Carbon\Carbon;
use Illuminate\Auth\UserInterface;
use Illuminate\Auth\Reminders\RemindableInterface;
class User extends Eloquent implements UserInterface, RemindableInterface{
use ConfideUser;
use HasRole;
/**
* Get user by username
* #param $username
* #return mixed
*/
public function getUserByUsername( $username )
{
return $this->where('username', '=', $username)->first();
}
public function joined()
{
return String::date(Carbon::createFromFormat('Y-n-j G:i:s', $this->created_at));
}
public function saveRoles($inputRoles)
{
if(! empty($inputRoles)) {
$this->roles()->sync($inputRoles);
} else {
$this->roles()->detach();
}
}
public function currentRoleIds()
{
$roles = $this->roles;
$roleIds = false;
if( !empty( $roles ) ) {
$roleIds = array();
foreach( $roles as &$role )
{
$roleIds[] = $role->id;
}
}
return $roleIds;
}
public static function checkAuthAndRedirect($redirect, $ifValid=false)
{
// Get the user information
$user = Auth::user();
$redirectTo = false;
if(empty($user->id) && ! $ifValid) // Not logged in redirect, set session.
{
Session::put('loginRedirect', $redirect);
$redirectTo = Redirect::to('user/login')
->with( 'notice', Lang::get('user/user.login_first') );
}
elseif(!empty($user->id) && $ifValid) // Valid user, we want to redirect.
{
$redirectTo = Redirect::to($redirect);
}
return array($user, $redirectTo);
}
public function currentUser()
{
return (new Confide(new ConfideEloquentRepository()))->user();
}
public function getReminderEmail()
{
return $this->email;
}
}
Role:
<?php
use Zizaco\Entrust\EntrustRole;
class Role extends EntrustRole {
public function validateRoles( array $roles )
{
$user = Confide::user();
$roleValidation = new stdClass();
foreach( $roles as $role )
{
// Make sure theres a valid user, then check role.
$roleValidation->$role = ( empty($user) ? false : $user->hasRole($role) );
}
return $roleValidation;
}
}
Permission:
<?php
use Zizaco\Entrust\EntrustPermission;
class Permission extends EntrustPermission
{
public function preparePermissionsForDisplay($permissions)
{
// Get all the available permissions
$availablePermissions = $this->all()->toArray();
foreach($permissions as &$permission) {
array_walk($availablePermissions, function(&$value) use(&$permission){
if($permission->name == $value['name']) {
$value['checked'] = true;
}
});
}
return $availablePermissions;
}
/**
* Convert from input array to savable array.
* #param $permissions
* #return array
*/
public function preparePermissionsForSave( $permissions )
{
$availablePermissions = $this->all()->toArray();
$preparedPermissions = array();
foreach( $permissions as $permission => $value )
{
// If checkbox is selected
if( $value == '1' )
{
// If permission exists
array_walk($availablePermissions, function(&$value) use($permission, &$preparedPermissions){
if($permission == (int)$value['id']) {
$preparedPermissions[] = $permission;
}
});
}
}
return $preparedPermissions;
}
}
Furthermore, I would like to seed my database in the beginning with values, therefore I created several seeders for user, role and permission. However, I get an error in my permission seeder:
UserTableSeeder:
<?php
class UsersTableSeeder extends Seeder {
public function run()
{
DB::table('users')->delete();
$users = array(
array(
'username' => 'admin',
'email' => 'admin#example.org',
'password' => Hash::make('admin'),
'confirmed' => 1,
'confirmation_code' => md5(microtime().Config::get('app.key')),
'created_at' => new DateTime,
'updated_at' => new DateTime,
),
array(
'username' => 'moderator',
'email' => 'moderator#example.org',
'password' => Hash::make('moderator'),
'confirmed' => 1,
'confirmation_code' => md5(microtime().Config::get('app.key')),
'created_at' => new DateTime,
'updated_at' => new DateTime,
),
array(
'username' => 'user',
'email' => 'user#example.org',
'password' => Hash::make('user'),
'confirmed' => 1,
'confirmation_code' => md5(microtime().Config::get('app.key')),
'created_at' => new DateTime,
'updated_at' => new DateTime,
)
);
DB::table('users')->insert( $users );
}
}
RolesTableSeeder:
<?php
class RolesTableSeeder extends Seeder {
public function run()
{
DB::table('roles')->delete();
$adminRole = new Role;
$adminRole->name = 'adminRole';
$adminRole->save();
$standRole = new Role;
$standRole->name = 'userRole';
$standRole->save();
$modRole = new Role;
$modRole->name = 'modRole';
$modRole->save();
$user = User::where('username','=','admin')->first();
$user->attachRole( $adminRole );
$user = User::where('username','=','user')->first();
$user->attachRole( $standRole );
$user = User::where('username','=','moderator')->first();
$user->attachRole( $modRole );
}
}
PermissionsTableSeeder:
<?php
class PermissionsTableSeeder extends Seeder {
public function run()
{
DB::table('permissions')->delete();
$permissions = array(
array( // 1
'name' => 'manage_users',
'display_name' => 'manage users'
),
array( // 2
'name' => 'manage_roles',
'display_name' => 'manage roles'
),
array( // 3
'name' => 'standart_user_role',
'display_name' => 'standart_user_role'
),
);
DB::table('permissions')->insert( $permissions );
DB::table('permission_role')->delete();
$role_id_admin = Role::where('name', '=', 'admin')->first()->id;
$role_id_mod = Role::where('name', '=', 'moderator')->first()->id;
$role_id_stand = Role::where('name', '=', 'user')->first()->id;
$permission_base = (int)DB::table('permissions')->first()->id - 1;
$permissions = array(
array(
'role_id' => $role_id_admin,
'permission_id' => $permission_base + 1
),
array(
'role_id' => $role_id_admin,
'permission_id' => $permission_base + 2
),
array(
'role_id' => $role_id_mod,
'permission_id' => $permission_base + 1
),
array(
'role_id' => $role_id_mod,
'permission_id' => $permission_base + 3
),
array(
'role_id' => $role_id_stand,
'permission_id' => $permission_base + 3
),
);
DB::table('permission_role')->insert( $permissions );
}
}
This is the error I get when running db:seed:
$ php artisan db:seed
**************************************
* Application In Production! *
**************************************
Do you really wish to run this command? Y
Seeded: UsersTableSeeder
Seeded: RolesTableSeeder
[ErrorException]
Trying to get property of non-object
db:seed [--class[="..."]] [--database[="..."]] [--force]
Any recommendations what I am doing wrong in my seeding?
I appreciate your answers!
I think the problem is with:
$role_id_admin = Role::where('name', '=', 'admin')->first()->id;
$role_id_mod = Role::where('name', '=', 'moderator')->first()->id;
$role_id_stand = Role::where('name', '=', 'user')->first()->id;
You want to get id but such records don't exists so you get the error. There are probably no records with admin, moderator or user name because looking at RolesTableSeeder you create roles this way:
$adminRole = new Role;
$adminRole->name = 'adminRole';
$adminRole->save();
$standRole = new Role;
$standRole->name = 'userRole';
$standRole->save();
$modRole = new Role;
$modRole->name = 'modRole';
$modRole->save();
with names adminRole, userRole, modRole. So either change the names inside RolesTableSeeder or change them in PermissionsTableSeeder

How to create form inputs/elements in ZF2

EDIT : My main question has now become 'How do I get the ServiceManager with the doctrine entity manager into the hands of my form, element, and input classes in some clean way?' Read on to see the full post.
I'm going to try and ask by example here so bear with me. Let me know where I'm going wrong/right or where I could improve
I'm trying to create a registration form. I could use ZfcUser module but I want to do this on my own. I'm using ZF2 with Doctrine2 as well so that leads me away from that module a bit.
My strategy was this,
Create a form class called registration form
Create separate 'element' classes for each element where each element will have an input specification
Since each element is a separate class from the form I can unit test each one separately.
All seemed fine until I wanted to add a validator to my username element that would check that the username is NOT is use yet.
Here is the code thus far
namepsace My\Form;
use Zend\Form\Form,
Zend\Form\Element,
Zend\InputFilter\Input,
Zend\InputFilter\InputFilter,
/**
* Class name : Registration
*/
class Registration
extends Form
{
const USERNAME = 'username';
const EMAIL = 'email';
const PASSWORD = 'password';
const PASS_CONFIRM = 'passwordConfirm';
const GENDER = 'gender';
const CAPTCHA = 'captcha';
const CSRF = 'csrf';
const SUBMIT = 'submit';
private $captcha = 'dumb';
public function prepareForm()
{
$this->setName( 'registration' );
$this->setAttributes( array(
'method' => 'post'
) );
$this->add( array(
'name' => self::USERNAME,
'type' => '\My\Form\Element\UsernameElement',
'attributes' => array(
'label' => 'Username',
'autofocus' => 'autofocus'
)
)
);
$this->add( array(
'name' => self::SUBMIT,
'type' => '\Zend\Form\Element\Submit',
'attributes' => array(
'value' => 'Submit'
)
) );
}
}
I removed a lot that I think isn't necessary. Here is my username element below.
namespace My\Form\Registration;
use My\Validator\UsernameNotInUse;
use Zend\Form\Element\Text,
Zend\InputFilter\InputProviderInterface,
Zend\Validator\StringLength,
Zend\Validator\NotEmpty,
Zend\I18n\Validator\Alnum;
/**
*
*/
class UsernameElement
extends Text
implements InputProviderInterface
{
private $minLength = 3;
private $maxLength = 128;
public function getInputSpecification()
{
return array(
'name' => $this->getName(),
'required' => true,
'filters' => array(
array( 'name' => 'StringTrim' )
),
'validators' =>
array(
new NotEmpty(
array( 'mesages' =>
array(
NotEmpty::IS_EMPTY => 'The username you provided is blank.'
)
)
),
new AlNum( array(
'messages' => array( Alnum::STRING_EMPTY => 'The username can only contain letters and numbers.' )
)
),
new StringLength(
array(
'min' => $this->getMinLength(),
'max' => $this->getMaxLength(),
'messages' =>
array(
StringLength::TOO_LONG => 'The username is too long. It cannot be longer than ' . $this->getMaxLength() . ' characters.',
StringLength::TOO_SHORT => 'The username is too short. It cannot be shorter than ' . $this->getMinLength() . ' characters.',
StringLength::INVALID => 'The username is not valid.. It has to be between ' . $this->getMinLength() . ' and ' . $this->getMaxLength() . ' characters long.',
)
)
),
array(
'name' => '\My\Validator\UsernameNotInUse',
'options' => array(
'messages' => array(
UsernameNotInUse::ERROR_USERNAME_IN_USE => 'The usarname %value% is already being used by another user.'
)
)
)
)
);
}
}
Now here is my validator
namespace My\Validator;
use My\Entity\Helper\User as UserHelper,
My\EntityRepository\User as UserRepository;
use Zend\Validator\AbstractValidator,
Zend\ServiceManager\ServiceManagerAwareInterface,
Zend\ServiceManager\ServiceLocatorAwareInterface,
Zend\ServiceManager\ServiceManager;
/**
*
*/
class UsernameNotInUse
extends AbstractValidator
implements ServiceManagerAwareInterface
{
const ERROR_USERNAME_IN_USE = 'usernameUsed';
private $serviceManager;
/**
*
* #var UserHelper
*/
private $userHelper;
protected $messageTemplates = array(
UsernameNotInUse::ERROR_USERNAME_IN_USE => 'The username you specified is being used already.'
);
public function isValid( $value )
{
$inUse = $this->getUserHelper()->isUsernameInUse( $value );
if( $inUse )
{
$this->error( UsernameNotInUse::ERROR_USERNAME_IN_USE, $value );
}
return !$inUse;
}
public function setUserHelper( UserHelper $mapper )
{
$this->userHelper = $mapper;
return $this;
}
/**
* #return My\EntityRepository\User
*/
public function getUserHelper()
{
if( $this->userHelper == null )
{
$this->setUserHelper( $this->getServiceManager()->get( 'doctrine.entitymanager.orm_default' )->getObjectRepository( 'My\Entity\User') );
}
return $this->userHelper;
}
public function setServiceManager( ServiceManager $serviceManager )
{
echo get_class( $serviceManager );
echo var_dump( $serviceManager );
$this->serviceManager = $serviceManager;
return $this;
}
/**
*
* #return ServiceManager
*/
public function getServiceManager( )
{
return $this->serviceManager;
}
}
Why did this seem like a good idea to me?
It seemed like a good testability/re-use choice to make since I could re-use the elements separately across my application if need be.
I could unit test each Input generated by each element to make sure it correctly accepts/rejects input.
This is the example of my unit test for the element
public function testFactoryCreation()
{
$fac = new Factory();
$element = $fac->createElement( array(
'type' => '\My\Form\Registration\UsernameElement'
) );
/* #var $element \My\Form\Registration\UsernameElement */
$this->assertInstanceOf( '\My\Form\Registration\UsernameElement',
$element );
$input = $fac->getInputFilterFactory()->createInput( $element->getInputSpecification() );
$validators = $input->getValidatorChain()->getValidators();
/* #var $validators \Zend\Validator\ValidatorChain */
$expectedValidators = array(
'Zend\Validator\StringLength',
'Zend\Validator\NotEmpty',
'Zend\I18n\Validator\Alnum',
'My\Validator\UsernameNotInUse'
);
foreach( $validators as $validator )
{
$actualClass = get_class( $validator['instance'] );
$this->assertContains( $actualClass, $expectedValidators );
switch( $actualClass )
{
case 'My\Validator\UsernameNotInUse':
$helper = $validator['instance']->getUserHelper();
//HAVING A PROBLEM HERE
$this->assertNotNull( $helper );
break;
default:
break;
}
}
}
The problem I'm having is that the validator can't fetch the UserHelper properly, which is really a UserRepository from doctrine. The reason this is happening is because the validators only get access to the ValidatorPluginManager as a ServiceManager rather than having access to the application wide ServiceManager.
I get this error for the Validator portion, although if I call the same get method on the general service manager it works with no problems.
1) Test\My\Form\Registration\UsernameElementTest::testFactoryCreation
Zend\ServiceManager\Exception\ServiceNotFoundException: Zend\ServiceManager\ServiceManager::get was unable to fetch or create an instance for doctrine.entitymanager.orm_default
The var_dump( $serviceManager ) in validator shows me it is of the class ValidatorPluginManager.
I tried putting a factory in the service_manager entry like so
'service_manager' => array(
'factories' => array(
'My\Validator\UsernameNotInUse' => function( $sm )
{
$validator = new \My\Validator\UsernameNotInUse();
$em = $serviceManager->get( 'doctrine.entitymanager.orm_default' );
/* #var $em \Doctrine\ORM\EntityManager */
$validator->setUserHelper( $em->getRepository( '\My\Entity\User' ) );
return $validator;
}
)
but that didn't work because it's not consulting the application level service manager.
So, overall, here are my questions :
Is this strategy of separating the form and elements a good one? Should I keep going this way? What are alternatives? ( I'm for breaking stuff up for the sake of testability ) I was going to test ONLY the form itself originally with a combination of ALL the inputs but it seemed like I'd be trying to do too much.
How do I resolve the issue I have above?
Should I be using the Form/Element/Input parts of Zend in some other way that I'm not seeing?
this is my validator, using a static method to inject the entityManager and working with any doctine entity.
<?php
namespace Base\Validator;
use Traversable;
use Zend\Stdlib\ArrayUtils;
use Zend\Validator\AbstractValidator;
use Doctrine\ORM\EntityManager;
class EntityUnique extends AbstractValidator
{
const EXISTS = 'exists';
protected $messageTemplates = array(
self::EXISTS => "A %entity% record already exists with %attribute% %value%",
);
protected $messageVariables = array(
'entity' => '_entity',
'attribute' => '_attribute',
);
protected $_entity;
protected $_attribute;
protected $_exclude;
protected static $_entityManager;
public static function setEntityManager(EntityManager $em) {
self::$_entityManager = $em;
}
public function getEntityManager() {
if (!self::$_entityManager) {
throw new \Exception('No entitymanager present');
}
return self::$_entityManager;
}
public function __construct($options = null)
{
if ($options instanceof Traversable) {
$options = ArrayUtils::iteratorToArray($token);
}
if (is_array($options)) {
if (array_key_exists('entity', $options)) {
$this->_entity = $options['entity'];
}
if (array_key_exists('attribute', $options)) {
$this->_attribute = $options['attribute'];
}
if (array_key_exists('exclude', $options)) {
if (!is_array($options['exclude']) ||
!array_key_exists('attribute', $options['exclude']) ||
!array_key_exists('value', $options['exclude'])) {
throw new \Exception('exclude option must contain attribute and value keys');
}
$this->_exclude = $options['exclude'];
}
}
parent::__construct(is_array($options) ? $options : null);
}
public function isValid($value, $context = null)
{
$this->setValue($value);
$queryBuilder = $this->getEntityManager()
->createQueryBuilder()
->from($this->_entity, 'e')
->select('COUNT(e)')
->where('e.'. $this->_attribute . ' = :value')
->setParameter('value', $this->getValue());
if ($this->_exclude) {
$queryBuilder = $queryBuilder->andWhere('e.'. $this->_exclude['attribute'] . ' != :exclude')
->setParameter('exclude', $this->_exclude['value']);
}
$query = $queryBuilder->getQuery();
if ((integer)$query->getSingleScalarResult() !== 0) {
$this->error(self::EXISTS);
return false;
}
return true;
}
}
ie. i'm using it for theese form elements which are also tested and working fine:
<?php
namespace User\Form\Element;
use Zend\Form\Element\Text;
use Zend\InputFilter\InputProviderInterface;
class Username extends Text implements InputProviderInterface
{
public function __construct() {
parent::__construct('username');
$this->setLabel('Benutzername');
$this->setAttribute('id', 'username');
}
public function getInputSpecification() {
return array(
'name' => $this->getName(),
'required' => true,
'filters' => array(
array(
'name' => 'StringTrim'
),
),
'validators' => array(
array(
'name' => 'NotEmpty',
'break_chain_on_failure' => true,
'options' => array(
'messages' => array(
'isEmpty' => 'Bitte geben Sie einen Benutzernamen ein.',
),
),
),
),
);
}
}
When creating a new user
<?php
namespace User\Form\Element;
use Zend\InputFilter\InputProviderInterface;
use User\Form\Element\Username;
class CreateUsername extends Username implements InputProviderInterface
{
public function getInputSpecification() {
$spec = parent::getInputSpecification();
$spec['validators'][] = array(
'name' => 'Base\Validator\EntityUnique',
'options' => array(
'message' => 'Der name %value% ist bereits vergeben.',
'entity' => 'User\Entity\User',
'attribute' => 'username',
),
);
return $spec;
}
}
when editin an existing user
<?php
namespace User\Form\Element;
use Zend\InputFilter\InputProviderInterface;
use User\Form\Element\Username;
class EditUsername extends Username implements InputProviderInterface
{
protected $_userId;
public function __construct($userId) {
parent::__construct();
$this->_userId = (integer)$userId;
}
public function getInputSpecification() {
$spec = parent::getInputSpecification();
$spec['validators'][] = array(
'name' => 'Base\Validator\EntityUnique',
'options' => array(
'message' => 'Der name %value% ist bereits vergeben.',
'entity' => 'User\Entity\User',
'attribute' => 'username',
'exclude' => array(
'attribute' => 'id',
'value' => $this->_userId,
),
),
);
return $spec;
}
}

Categories