custom validation in YII2 rule is not working - yii

I have done this in my model:
public function rules() {
return [
[['category_name'], 'required','message' => 'Please enter {attribute}.'],
[['category_name'], 'string', 'max' => 45],
[['category_name'], 'safe', 'on' => 'search'],
['category_name', 'checkName'],
];
}
public function checkName($attribute, $params) {
$model = Categories::find()->where('category_name = "' . $this->$attribute . '" AND status != ' . Categories::STATUS_DELETED)->all(); //STATUS_DELETED = 2 constant.
if (count($model) > 0) {
$this->addError($attribute, 'Category name is already exists.');
}
}
The problem is that checkName function is not triggering when entering the duplicate value. what could be the possible reason?

ok there was a minor issue, actually the table name is categories and model is just category so changing model name to category solved the issue.

Related

How to add captcha required only for a particular condition yii2

I am trying make the captcha field required only when the number of failed login attempts exceed 3 times. For which I have written below code till now.
In LoginForm model I have added the below rules
public function rules()
{
return [
[['username', 'password'], 'required'],
['password', 'validatePassword'],
['verifyCode', 'captcha', 'when' => function($model) {
return $this->checkattempts();
}],
];
}
public function validatePassword($attribute, $params)
{
if (!$this->hasErrors()) {
$user = $this->getUser();
if (!$user || !$user->validatePassword($this->password)) {
$this->addLoginAttempt($user->id);
$this->addError($attribute, 'Incorrect username or password.');
}
}
}
public function checkattempts()
{
$user = $this->getUser();
$ip = $this->get_client_ip();
$data = (new Query())->select('*')
->from('login_attempts')
->where(['ip' => $ip])->andWhere(['user_ref_id' => $user->id])
->one();
if($data["attempts"] >=3){
return false;
}else{
return true;
}
}
public function addLoginAttempt($uid) {
$ip = $this->get_client_ip();
$data = (new Query())->select('*')
->from('login_attempts')
->where(['ip' => $ip])->andWhere(['user_ref_id' => $uid])
->one();
if($data)
{
$attempts = $data["attempts"]+1;
#Yii::$app->db->createCommand("UPDATE login_attempts SET attempts=".$attempts." where ip = '$ip' AND user_ref_id=$uid")->execute();
}
else {
Yii::$app->db->createCommand("INSERT into login_attempts (attempts, user_ref_id, ip) VALUES(1,'$uid', '$ip')")->execute();
}
}
Here I am validating the password first. If the password is incorrect then I am incrementing the count by 1. This part is working fine. The count is incrementing successfully.
After this I am trying to get the count of failed attempts while validating captcha using the function checkattempts(), but it is not working.
Can anyone please tell me where I have made mistake.
Thanks in advance.
In your model:
if (!$model->checkattempts())
//show the captcha
Then, in your model rules you'll need something like:
['captcha', 'captcha'],
In your case, what you can do is use different scenarios depending on the user attempts, and in one scenario (more than X attempts) make the captcha required.
More documentation about the captcha and about scenarios.

Is my order complete return approach correct?

When a customer is returned to the following URL (example);
http://prestashop.dev/index.php?action=completed&controller=callback&fc=module&hmac={valid-hmac}&merchant_order_id=14&module=chippin
After a successful payment, It will call on this FrontController sub-class;
class ChippinCallbackModuleFrontController extends ModuleFrontController
{
public function postProcess()
{
$chippin = new Chippin();
$payment_response = new PaymentResponse();
$payment_response->getPostData();
// if a valid response from gateway
if(ChippinValidator::isValidHmac($payment_response)) {
// "action" is passed as a param in the URL. don't worry, the Hmac can tell if it's valid or not.
if ($payment_response->getAction() === "completed") {
// payment_response->getMerchantOrderId() will just return the id_order from the orders table
$order_id = Order::getOrderByCartId((int) ($payment_response->getMerchantOrderId()));
$order = new Order($order_id);
// this will update the order status for the benefit of the merchant.
$order->setCurrentState(Configuration::get('CP_OS_PAYMENT_COMPLETED'));
// assign variables to smarty (copied this from another gateway, don't really understand smarty)
$this->context->smarty->assign(
array(
'order' => $order->reference,
)
);
// display this template
$this->setTemplate('confirmation.tpl');
I'm quite new to Prestashop. I'm just not sure if this is technically done or not. The confirmation.tlp view does display with the order->reference and the order status is updated to "Completed" but is this all I need?
Are there any other considerations? I have the opportunity to call a hookDisplayPaymentReturn at this point but why should I?
I seem to have a pretty standard return page. Is this enough;
Update - Do I just call a hook something like;
public function displayPaymentReturn()
{
$params = $this->displayHook();
if ($params && is_array($params)) {
return Hook::exec('displayPaymentReturn', $params, (int) $this->module->id);
}
return false;
}
As far as I can see everything seems okay for me.
You should consider adding hookDisplayPaymentReturn it allows other modules to add code to your confirmation page. For example a Google module could add javascript code that sends order informations to analytics on confirmation page.
EDIT
class ChippinCallbackModuleFrontController extends ModuleFrontController
{
public function postProcess()
{
$chippin = new Chippin();
$payment_response = new PaymentResponse();
$payment_response->getPostData();
// if a valid response from gateway
if(ChippinValidator::isValidHmac($payment_response)) {
// "action" is passed as a param in the URL. don't worry, the Hmac can tell if it's valid or not.
if ($payment_response->getAction() === "completed") {
// payment_response->getMerchantOrderId() will just return the id_order from the orders table
$order_id = Order::getOrderByCartId((int) ($payment_response->getMerchantOrderId()));
$order = new Order($order_id);
// this will update the order status for the benefit of the merchant.
$order->setCurrentState(Configuration::get('CP_OS_PAYMENT_COMPLETED'));
// assign variables to smarty (copied this from another gateway, don't really understand smarty)
$this->context->smarty->assign(
array(
'order' => $order->reference,
'hookDisplayPaymentReturn' => Hook::exec('displayPaymentReturn', $params, (int) $this->module->id);
)
);
$cart = $this->context->cart;
$customer = new Customer($cart->id_customer);
Tools::redirect('index.php?controller=order-confirmation&id_cart='.$cart->id.'&id_module='.$this->module->id.'&id_order='.$order->id.'&key='.$customer->secure_key);
And in your module :
class myPaymentModule extends PaymentModule
{
public function install()
{
if (!parent::install() || !$this->registerHook('paymentReturn'))
return false;
return true;
}
// Example taken from bankwire module
public function hookPaymentReturn($params)
{
$state = $params['objOrder']->getCurrentState();
$this->smarty->assign(array(
'total_to_pay' => Tools::displayPrice($params['total_to_pay'], $params['currencyObj'], false),
'bankwireDetails' => Tools::nl2br($this->details),
'bankwireAddress' => Tools::nl2br($this->address),
'bankwireOwner' => $this->owner,
'status' => 'ok',
'id_order' => $params['objOrder']->id
));
if (isset($params['objOrder']->reference) && !empty($params['objOrder']->reference))
$this->smarty->assign('reference', $params['objOrder']->reference);
return $this->display(__FILE__, 'confirmation.tpl');
}
}

Prestashop product update hook is not updating product attributes in the hook function

I am using hookActionProductUpdate. I am getting all data updated but not attributes.
This is the code inside hook function:
public function hookActionProductUpdate($params) {
$prestaObject = new ProductCore($params['id_product'], false, Context::getContext()->language->id);
$arrrs = $prestaObject->getFrontFeatures(1);
}
Everything else is updated but the front features I am getting are the older one. Any IDEA?
EDIT: I tried this too, here is my new function:
public function hookActionProductUpdate($params) {
$product = $params['product'];
$arrrs = $product->getFrontFeatures(1);
pr($arrrs);die("No updating :(");
}
You don't need to instantiate a new Object. The product Object should already be contained in $params['product'].
Here is the update() method of Product Class where this Hook is called:
public function update($null_values = false)
{
$return = parent::update($null_values);
$this->setGroupReduction();
// Sync stock Reference, EAN13 and UPC
if (Configuration::get('PS_ADVANCED_STOCK_MANAGEMENT') && StockAvailable::dependsOnStock($this->id, Context::getContext()->shop->id)) {
Db::getInstance()->update('stock', array(
'reference' => pSQL($this->reference),
'ean13' => pSQL($this->ean13),
'upc' => pSQL($this->upc),
), 'id_product = '.(int)$this->id.' AND id_product_attribute = 0');
}
Hook::exec('actionProductSave', array('id_product' => (int)$this->id, 'product' => $this));
Hook::exec('actionProductUpdate', array('id_product' => (int)$this->id, 'product' => $this));
if ($this->getType() == Product::PTYPE_VIRTUAL && $this->active && !Configuration::get('PS_VIRTUAL_PROD_FEATURE_ACTIVE')) {
Configuration::updateGlobalValue('PS_VIRTUAL_PROD_FEATURE_ACTIVE', '1');
}
return $return;
}
You should then use this code instead:
public function hookActionProductUpdate($params) {
$product = $params['product'];
$arrrs = $product->getFrontFeatures(1);
}
Yeah I got it why, Its a bug in prestashop, It calls the hook AdminProductsController
Hook::exec('actionProductSave', array('id_product' => (int)$this->id, 'product' => $this));
Hook::exec('actionProductUpdate', array('id_product' => (int)$this->id, 'product' => $this));
from an update method which is called first then it executes the feature update code.
INSIDE processupdate function
I found this code
//this update method calls the HOOK and when this hook get executed it updates features in the database.
if ($object->update()) {
// If the product doesn't exist in the current shop but exists in another shop
if (Shop::getContext() == Shop::CONTEXT_SHOP && !$existing_product->isAssociatedToShop($this->context->shop->id)) {
$out_of_stock = StockAvailable::outOfStock($existing_product->id, $existing_product->id_shop_default);
$depends_on_stock = StockAvailable::dependsOnStock($existing_product->id, $existing_product->id_shop_default);
StockAvailable::setProductOutOfStock((int)$this->object->id, $out_of_stock, $this->context->shop->id);
StockAvailable::setProductDependsOnStock((int)$this->object->id, $depends_on_stock, $this->context->shop->id);
}
PrestaShopLogger::addLog(sprintf($this->l('%s modification', 'AdminTab', false, false), $this->className), 1, null, $this->className, (int)$this->object->id, true, (int)$this->context->employee->id);
if (in_array($this->context->shop->getContext(), array(Shop::CONTEXT_SHOP, Shop::CONTEXT_ALL))) {
if ($this->isTabSubmitted('Shipping')) {
$this->addCarriers();
}
if ($this->isTabSubmitted('Associations')) {
$this->updateAccessories($object);
}
if ($this->isTabSubmitted('Suppliers')) {
$this->processSuppliers();
}
if ($this->isTabSubmitted('Features')) {
$this->processFeatures();
}

Doctrine 2 issue inside Zf2 Navigation - strange issue

I can use doctrine from all my controller without any problem
$em = $this->getServiceLocator()->get("doctrine.entitymanager.orm_default");
$newsItems = $em->getRepository('Website\Entity\News')->findAll();
foreach($newsItems as $item)
// do stuff here
Preamble: if I use Zend\Db\Adapter instead of Doctrine the following work as aspected !
I'm inside a MyNavigationObject that was instanciated by MyNavigationFactory.
I have a mysql table called mainmenu with 4 records: home,news,company,contact
I would like to do my own navigation with Doctrine-Orm.
Also I have an entity called Mainmenu.
I can call the entityManager and the results is composed with 4 records but 4 times the records number 1. Incredible !
this is the code:
protected function getPages(ServiceLocatorInterface $serviceLocator)
{
if (null === $this->pages) {
$em = $serviceLocator->get("doctrine.entitymanager.orm_default");
$menuItems = $em->getRepository('Website\Entity\Mainmenu')->findAll();
//return default key
$configuration['navigation'][$this->getName()] = array();
foreach ($menuItems as $menuItem)
{
$configuration['navigation'][$this->getName()][$menuItem->getName()] = array(
'label' => $menuItem->getLabel(),
'route' => $menuItem->getRoute(),
'controller' => $menuItem->getController(),
);
}
if (!isset($configuration['navigation'])) {
throw new \Exception\InvalidArgumentException('Could not find navigation configuration key');
}
if (!isset($configuration['navigation'][$this->getName()])) {
throw new Exception\InvalidArgumentException(sprintf(
'Failed to find a navigation container by the name "%s"',
$this->getName()
));
}
// refer to Mvc::Application object not to modulename
$application = $serviceLocator->get('Application');
$routeMatch = $application->getMvcEvent()->getRouteMatch();
$router = $application->getMvcEvent()->getRouter();
$pages = $this->getPagesFromConfig($configuration['navigation'][$this->getName()]);
$this->pages = $this->injectComponents($pages, $routeMatch, $router);
}
return $this->pages;
Can anyone provide suggestions or reproduce the issue ?
bye

Customizing Xmultiselect in Yii

I am not able to make the left list of multiselect widget in empty mode on load. It shows error when I set null value to the left list. This is my code:
$this->widget('ext.widgets.multiselects.XMultiSelects', array(
'leftTitle' => '',
'leftName' => 'Certificate[selected][]',
'leftList' => SpecificCertification::model()->findCertificate(),// here I need to make the list empty
'rightTitle' => '',
'rightName' => 'Certificate[all][]',
'rightList' => SpecificCertification::model()->findCertificates(),
'size' => 10,
));
How can I make the left list empty ?
You need to open file widget XMultiSelects.php and modify it to fit your need
public function init()
{
/* Comment out the below validation
if(!isset($this->leftList))
{
throw new CHttpException(500,'"leftList" have to be set!');
}
if(!isset($this->rightList))
{
throw new CHttpException(500,'"rightList" have to be set!');
}
*/
}
Add validation for leftList and rightList such as below
if($this->leftList){
foreach($this->leftList as $value=>$label)
{
echo "<option value=\"{$value}\">{$label}</option>\n";
}
}
and
if($this->rightList){
foreach($this->rightList as $value=>$label)
{
echo "<option value=\"{$value}\">{$label}</option>\n";
}
}
After then, you can set null for them like what you did