Dynamically switching the error message on validation? - validation

With the new validator object - is it possible to replace the validation error inside the validation rule triggered? to not only return the static error message but maybe some dynamically genereted one?
public function validateLength($data) {
...
$length = mb_strlen($data['name']);
$this->validator()->getField('name')->setRule('validateLength', array('message' => $length . 'chars'));
...
}
does not work, of course (too late I guess)
I want to actually return the lenght of the string (you used 111 chars from 100 allowed) for example - but for this I would need to be able to replace the message from inside the (custom) validation method
$this->validate['name']['validateLength']['message'] = $length . 'chars';
also never worked so far. it would always return the previous (static) error message from the $validate array

public function customValidator($data) {
....
if ($validates) {
return true;
} else {
return 'my new error message';
}
}

The following snippet should do the trick:
public function validateLength($data) {
...
$length = mb_strlen($data['name']);
$this->validator()->getField('name')->getRule('validateLength')->message = $length . 'chars';
...
}

Related

Model events not fire in laravel testing

I'm writing some tests for my model when it was created/updated/deleted. When I test it manually, it worked. But when I test in test file, asserting a event dispatch always fail.
This is my test function:
public function test_booted_method_ran()
{
$model = AcCard::factory()->create();
$this->expectsEvents('eloquent.created: \App\Models\AcCard');
}
This test case always fails. I've tried to inspect expectsEvents and added dd() to check event list:
public function expectsEvents($events)
{
$events = is_array($events) ? $events : func_get_args();
$this->withoutEvents();
$this->beforeApplicationDestroyed(function () use ($events) {
$fired = $this->getFiredEvents($events);
dd($fired); // Added this code line
$this->assertEmpty(
$eventsNotFired = array_diff($events, $fired),
'These expected events were not fired: ['.implode(', ', $eventsNotFired).']'
);
});
return $this;
}
And result is an empty array.
What I tried
I tried to add this code lines above model creating
AcCard::flushEventListeners();
AcCard::boot();
AcCard::booted();
But it doesn't work.

Laravel help for optimize command script

I'm working with Lumen framework v5.8 (it's the same as Laravel)
I have a command for read a big XML (400Mo) and update datas in database from datas in this file, this is my code :
public function handle()
{
$reader = new XMLReader();
$reader->open(storage_path('app/mesh2019.xml'));
while ($reader->read()) {
switch ($reader->nodeType) {
case (XMLREADER::ELEMENT):
if ($reader->localName === 'DescriptorRecord') {
$node = new SimpleXMLElement($reader->readOuterXML());
$meshId = $node->DescriptorUI;
$name = (string) $node->DescriptorName->String;
$conditionId = Condition::where('mesh_id', $meshId)->first();
if ($conditionId) {
ConditionTranslation::where(['condition_id' => $conditionId->id, 'locale' => 'fr'])->update(['name' => $name]);
$this->info(memory_get_usage());
}
}
}
}
}
So, I have to find in the XML each DescriptorUI element, the value corresponds to the mesh_id attribute of my class Condition.
So, with $conditionId = Condition::where('mesh_id', $meshId)->first(); I get the Condition object.
After that, I need to update a child of Condition => ConditionTranslation. So I just get the element DescriptorName and update the name field of ConditionTranslation
At the end of the script, you can see $this->info(memory_get_usage());, and when I run the command the value increases each time until the script runs very very slowly...and never ends.
How can I optimize this script ?
Thanks !
Edit : Is there a way with Laravel for preupdate multiple object, and save just one time at the end all objects ? Like the flush() method of Symfony
There is a solution with ON DUPLICATE KEY UPDATE
public function handle()
{
$reader = new XMLReader();
$reader->open(storage_path('app/mesh2019.xml'));
$keyValues = [];
while ($reader->read()) {
switch ($reader->nodeType) {
case (XMLREADER::ELEMENT):
if ($reader->localName === 'DescriptorRecord') {
$node = new SimpleXMLElement($reader->readOuterXML());
$meshId = $node->DescriptorUI;
$name = (string) $node->DescriptorName->String;
$conditionId = Condition::where('mesh_id', $meshId)->value('id');
if ($conditionId) {
$keyValues[] = "($conditionId, '".str_replace("'","\'",$name)."')";
}
}
}
}
if (count($keyValues)) {
\DB::query('INSERT into `conditions` (id, name) VALUES '.implode(', ', $keyValues).' ON DUPLICATE KEY UPDATE name = VALUES(name)');
}
}

Magento2: Argument 1 [...] must be an instance of Magento\Framework\App\Helper\Context

First of all, I'm quite new to Magento 2, but I've used Magento 1.x for some time.
I've read a lot about how to solve DI-related problems, but I'm stuck on this one:
Exception #0 (Exception): Recoverable Error: Argument 1 passed to Cefar\AO\Helper\Ao::__construct() must be an instance of Magento\Framework\App\Helper\Context, instance of Magento\Framework\ObjectManager\ObjectManager given, called in .../vendor/magento/framework/ObjectManager/Factory/AbstractFactory.php on line 93 and defined in .../Cefar/AO/Helper/Ao.php on line 11
Many other answers have suggested deleting the var/di and var/generation folders, sometimes var/cache also. While this solves the problem, it occurs again once bin/magento setup:di:compile is run, which means the code cannot be used in a production environment.
I've checked that the Ao class does not instantiate any objects. It also doesn't try to re-make any objects that could be provided by the context given. Here's the code:
namespace Cefar\AO\Helper;
class Ao extends \Magento\Framework\App\Helper\AbstractHelper
{
const DEFAULT_GRID_COLS = 4;
protected $_session;
public function __construct(
\Magento\Framework\App\Helper\Context $context,
\Magento\Customer\Model\Session $session
)
{
parent::__construct($context);
$this->_session = $session;
}
public function getConfig($path)
{
return $this->scopeConfig->getValue($path);
}
public function isActive($url = null, $print = true) {
$active = ($url && strstr($_SERVER['REQUEST_URI'], $url) !== false);
if ($active && $print) {
echo "active";
} else {
return $active;
}
}
public function isLoggedIn()
{
return $this->_session->isLoggedIn();
}
public function limitWords($text = '', $limit = 10, $showDots = true)
{
$words = explode(' ', $text);
$limited = array_slice($words, 0, $limit);
$newText = implode(' ', $limited);
if (count($words) > $limit && $showDots) {
$newText .= '...';
}
return $newText;
}
public function getCurrentGrid()
{
return ($this->_getRequest()->getParam('grid'))
? $this->_getRequest()->getParam('grid')
: self::DEFAULT_GRID_COLS;
}
}
There's nothing particularly special here. I'm confused as to how this is even happening; every other defined class in the extension is getting its DI parameters correctly. Why is the ObjectManager apparatus providing an unwanted argument? The relevant call is given in the error report as:
.../vendor/magento/framework/ObjectManager/Factory/AbstractFactory.php(93): Cefar\AO\Helper\Ao->__construct(Object(Magento\Framework\ObjectManager\ObjectManager))
So it isn't even providing two arguments!
I've also read about providing type hints in a di.xml, but it doesn't seem to be relevant here as both types are part of the Magento libraries? I note that there is an entry for Magento\Framework\App\Helper\Context but not one for Magento\Customer\Model\Session... but that there are framework classes that use ID to import Magento\Customer\Model\Session already which work.
Long story short, this was because of a typo.
Sometimes when the helper was being included, it was being referred to as Cefar\AO\Helper\Ao, and other times, Cefar\AO\Helper\AO. Essentially, the ObjectManager was resolving both of these references to the same class, but it only had type hints for one of the names so it didn't know what to provide to the incorrect one.
A little help would have been nice, Magento! Maybe an error report that the requested class wasn't found? Still, at least this is finally over with.

Doctrine is converting string to hex value

I am converting one database to another database through a script.
While running the script I am getting the following error:
SQLSTATE[HY000]: General error: 1366 Incorrect string value: '\xBFbangl...' for column 'country' at row 1
An exception is thrown where it shows that it tries to set country as "\xcf\xbb\xbf\x62\x61\x6e\x67\x6c\x61\x64\x65\x73\x68".
When I var_dump the value, it just shows as "Bangladesh".
Is there something I have to adjust in my php code?
I have tried this, but phpmyadmin is throwing an #1064 error stating a syntax error in the query.
UPDATE
The scripts is a command in Symfony3:
<?php
namespace My\Bundle\Command;
use My\AccommodationBundle\Entity\Visit;
use My\NewAccommodationBundleV2\Entity\Visit as Visit2;
use My\OtherBundle\Entity\Person;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class VisitConvertToNewFormatCommand extends ContainerAwareCommand
{
private $em;
protected function configure()
{
$this
->setName('accommodation:visit:convert')
->setDescription("Converting old structure to new structure'")
;
}
protected function getTimeStamp() {
$currentTime = new \DateTime('now');
return '['.$currentTime->format('Y-m-d H:i:s').'] ';
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$output->writeln($this->getTimeStamp().$this->getDescription());
$this->em = $this->getContainer()->get('doctrine')->getManager();
$visits = $this->em->getRepository('MyOldAccommodationBundle:Visit')->findAll();
foreach ($visits as $visit) {
$visit2 = new Visit2();
$visit2->setArrivalDate($visit->getArrivalDate());
$visit2->setArrivalStatus($visit->getArrivalStatus());
$visit2->setArrivalTime($visit->getArrivalTime());
$visit2->setBookingType($visit->getBookingType());
$visit2->setCountry($visit->getCountry()); // <----
...
$this->em->persist($visit2);
$user = $visit->getUser();
if ($user != null) {
$person = new Person();
...
$person->setFirstName(trim(ucfirst(strtolower($user->getFirstName()))));
$person->setLastName(trim(ucfirst(strtolower($user->getLastName()))));
$person->setEmail(preg_replace('/\s+/', '', $user->getEmail()));
...
$this->em->persist($person);
}
}
}
}
\x62\x61\x6e\x67\x6c\x61\x64\x65\x73\x68 is Bangladesh; the \xcf\xbb\xbf before it makes no sense. In latin1 it is Ï»¿. It does not validly convert to utf8. CFBB is ϻ (GREEK SMALL LETTER SAN), but then bf is broken utf8.
I suggest it is coming from the source of the data.
var_dump probably showed nothing because of what device you were displaying it on.
Ok after days of trying this did the trick:
$country = iconv("UTF-8", "ISO-8859-1//IGNORE", $country);
I have no idea why it went wrong in the first place. if anybody knows, please share.

How to create a slug from title and subtitle in Laravel 5

I want to create a slug. it should be a concatenation of title and subtitle.The following is my code and it's not working, i don't know where i went wrong?
public function setTitleAttribute($value)
{
$this->attributes['main_title'] = ucfirst($value);
$this->attributes['sub_title'] = $value;
if (! $this->exists) {
$this->attributes['slug'] = str_slug($this->attributes['main_title'].$this->attributes['sub_title']);
}
}
I need slug as a combination of main_title+sub_title
public function to_slug ($string) {
$table = array(
'Š'=>'S', 'ı'=>'i', 'ğ'=>'g', 'ü'=>'u', 'ş'=>'s', 'ö'=>'o', 'ç'=>'c', 'Ğ'=>'G', 'Ü'=>'U', 'Ş'=>'S',
'İ'=>'I', 'Ö'=>'O', 'Ç'=>'C',
'š'=>'s', 'Đ'=>'Dj', 'đ'=>'dj', 'Ž'=>'Z', 'ž'=>'z', 'Č'=>'C', 'č'=>'c', 'Ć'=>'C', 'ć'=>'c',
'À'=>'A', 'Á'=>'A', 'Â'=>'A', 'Ã'=>'A', 'Ä'=>'A', 'Å'=>'A', 'Æ'=>'A', 'Ç'=>'C', 'È'=>'E', 'É'=>'E',
'Ê'=>'E', 'Ë'=>'E', 'Ì'=>'I', 'Í'=>'I', 'Î'=>'I', 'Ï'=>'I', 'Ñ'=>'N', 'Ò'=>'O', 'Ó'=>'O', 'Ô'=>'O',
'Õ'=>'O', 'Ö'=>'O', 'Ø'=>'O', 'Ù'=>'U', 'Ú'=>'U', 'Û'=>'U', 'Ü'=>'U', 'Ý'=>'Y', 'Þ'=>'B', 'ß'=>'Ss',
'à'=>'a', 'á'=>'a', 'â'=>'a', 'ã'=>'a', 'ä'=>'a', 'å'=>'a', 'æ'=>'a', 'ç'=>'c', 'è'=>'e', 'é'=>'e',
'ê'=>'e', 'ë'=>'e', 'ì'=>'i', 'í'=>'i', 'î'=>'i', 'ï'=>'i', 'ð'=>'o', 'ñ'=>'n', 'ò'=>'o', 'ó'=>'o',
'ô'=>'o', 'õ'=>'o', 'ö'=>'o', 'ø'=>'o', 'ù'=>'u', 'ú'=>'u', 'û'=>'u', 'ý'=>'y', 'ý'=>'y', 'þ'=>'b',
'ÿ'=>'y', 'Ŕ'=>'R', 'ŕ'=>'r',
);
return preg_replace('/[^A-Za-z0-9-]+/', '-', strtr($string, $table) );
}
I'm using this code and its working for me.
It is also good for utf-8.
You can use a laravel package:
https://github.com/cviebrock/eloquent-sluggable
Or I use JavaScript when I sent the form:
https://github.com/madflow/jquery-slugify
I have replicated the exact condition like yours passing static data and its working perfectly in my machine.. try below solution.. do some dd and see whats the combination of the main_title and sub_title is resulting. Also check by removing that if condition once.
$this->attributes['main_title'] = ucfirst($value) . " ";
$this->attributes['sub_title'] = $value;
$slugToUse = $this->attributes['main_title'] . $this->attributes['sub_title'];
if (! $this->exists) {
$this->attributes['slug'] = str_slug($slugToUse);
}
}

Resources