Form validation with custom callback function - codeigniter

I created a "callback" function to check if the username exists in the DB.
I have multiple rules for the "username" field, but the only thing that work is my callback function. It refuses to check against the other rules. I tried leaving the field empty, and the "required" rule never kicked in.
Controller:
account.php
function register() {
$this->load->library('validation');
$fields['username'] = "trim|required|callback_username_check";
etc ...
etc ...
$this->validation->set_rules($fields);
if ($this->validation->run()) {
$records = array();
$records['username'] = $this->validation->username;
etc ...
etc ...
$data = $this->account_model->registerNewAccount($records);
}
$this->load->view('register_view');
}
function username_check($username) {
$m = new Mongo();
$collection = $m->selectDB( DBNAME )->selectCollection( TABLE );
$data = $collection->count(array("username" => $username) );
if($data == 1) {
$this->validation->set_message('username_check', '%s is already taken!');
return false;
} else {
return true;
}
}

Try using the new form_validation class here:
http://ellislab.com/codeigniter/user_guide/libraries/form_validation.html
I believe there was a bug about it.

Related

Laravel Multiple Edit/Upload File

I want to make my code more efficient in my Controller. This code is about to update file in the database and then delete the file were chosen, rather than use if-condition i will use switch statement and then call the updateFile function for each case of file name. But i have problem on my switch statement, it supposed to run for each case but doesn't.
private function updateFile($strFileName, $oldFileName){
if($request->file($strFileName)){
if($request->$oldFIleName){
Storage::delete($request->$oldFIleName);
}
$validatedData[$strFileName] = $request->file($strFileName)->store('post-files');
}
}
public function update(Request $request, Task $task)
{
//storing files request
$rules = [
'file_jamlak' => 'mimes:pdf,png,jpg|file|max:4096',
'file_kontrak' => 'mimes:pdf,png,jpg|file|max:4096',
'file_jamuk' => 'mimes:pdf,png,jpg|file|max:4096']
//validate rules in to new variable
$validatedData = $request->validate($rules);
//the switch
$file_name = $request->file();
switch($file_name){
case 'file_jamlak' : $this->updateFile('file_jamlak', 'oldJamlak');
}
//condition
//jamlak
if($request->file('file_jamlak')){
if($request->oldJamlak){
Storage::delete($request->oldJamlak);
// return $request->oldJamlak;
}
$validatedData['file_jamlak'] = $request->file('file_jamlak')->store('post-files');
$validatedData['jamlak'] = 1;
}
//kontrak
if($request->file('file_kontrak')){
if($request->oldKontrak){
Storage::delete($request->oldKontrak);
}
$validatedData['file_kontrak'] = $request->file('file_kontrak')->store('post-files');
$validatedData['kontrak'] = 1;
}
//add user id
$validatedData['user_id'] = auth()->user()->id;
//update eloquent
Task::where('id', $task->id)
->update($validatedData);
//dd($file_name);
return redirect('/admin/tasks')->with('success', 'New post has been updated!');
}
i think i made mistake on the $file_name, that it supposed to store file name but doesn't work. Please help me
Make sure you've file name in your case which you are checking against:
// ...
//the switch
$file_name = $request->file();
switch($file_name){
// make sure this name matches the name of the file you are submitting
// from the form on front end to be able to fall into this case
case 'file_jamlak' : $this->updateFile('file_jamlak', 'oldJamlak');
// ...

Yii2 Activerecord not saved before redirect and shown in "view"

Yii2 framework. When I save multiple ActiveRecords in AFTER_INSERT_EVENT of another ActiveRecord, the values in the database is not updated fast enough, so old values are shown when redirect to viewing the data.
To be more specific: Standard XAMPP environment with PHP 7.2.9. I have made a trait to make it easy to have extra attributes with history in model (either existing attributes or new attributes). The trait is used on ActiveRecord.
Notice the sleep(5) in function TL_save. This handled the problem, but it is not the correct solution. How do I ensure all is updated before it is read again? I want to avoid use locks on the row as that would require alteration of a table before it can be used. Is there a way around it? Transactions - I have tried it but perhaps not correct as it had no effect. A reload of the view page also solves the problem, but again: not very classy :-)
Also: Should I share this code on GitHub? I have not done so before and are not quite sure if it would be of any value to others really.
trait TimelineTrait
{
private $timelineConfig;
public function timelineInit($config)
{
$std = [
'attributes' => [], // required
'_oldAttributes'=>[],
'datetime'=> date('Y-m-d H:i:s'),
'validationRule'=>'safe',
'table'=>$this->tableName(),
'onlyDirty'=>true, // using !=, not !==
'events'=>[
self::EVENT_AFTER_INSERT=>[$this, 'TL_EventAfterInsert'],
self::EVENT_AFTER_UPDATE=>[$this, 'TL_EventAfterUpdate'],
self::EVENT_AFTER_FIND=>[$this, 'TL_EventAfterFind'],
self::EVENT_AFTER_DELETE=>[$this, 'TL_EventAfterDelete'],
],
'TimelineClass'=>Timeline::class,
/*
Must have the following attributes
id integer primary key auto increment not null,
table varchar(64) not null,
table_id integer not null,
attribute varchar(64) not null,
datetime datetime not null
value text (can be null)
*/
];
$this->timelineConfig = array_replace_recursive($std, $config);
foreach($this->timelineConfig["events"]??[] as $trigger=>$handler)
$this->on($trigger, $handler);
}
public function __get($attr)
{
$cfg = &$this->timelineConfig;
if (in_array($attr, array_keys($cfg["attributes"])))
return $cfg["attributes"][$attr];
else
return parent::__get($attr);
}
public function __set($attr, $val)
{
$cfg = &$this->timelineConfig;
if (in_array($attr, array_keys($cfg["attributes"]))) {
$cfg["attributes"][$attr] = $val;
} else
parent::__set($attr, $val);
}
public function attributes()
{
return array_merge(parent::attributes(), $this->timelineConfig["attributes"]);
}
public function rules()
{
$temp = parent::rules();
$temp[] = [array_keys($this->timelineConfig["attributes"]), $this->timelineConfig["validationRule"]];
return $temp;
}
public function TL_EventAfterInsert($event)
{
$this->TL_save($event, true);
}
public function TL_EventAfterUpdate($event)
{
$this->TL_save($event, false);
}
private function TL_save($event, $insert)
{
$cfg = &$this->timelineConfig;
if ($cfg["onlyDirty"])
$cfg["_oldAttributes"] = $this->TL_attributesOnTime();
foreach($cfg["attributes"] as $attr=>$val) {
$a = [
'table'=>$cfg["table"],
'table_id'=>$this->id,
'attribute'=>$attr,
'datetime'=>$cfg["datetime"],
];
if ($insert)
$model=null;
else
$model = Timeline::find()->where($a)->one();
$isNew = empty($model); // this exact attribute does not exist on timeline already
if ($isNew)
$model = new $cfg["TimelineClass"]($a);
$model->value = $val;
if (!$cfg["onlyDirty"]
|| $cfg["onlyDirty"] && $model->value!=($cfg["_oldAttributes"][$attr]??\uniqid('force_true'))) {
$ok = $model->save();
if (!$ok) $this->addErrors($attr, $model->getErrorSummary());
}
}
sleep(5);
}
public function TL_EventAfterFind($event)
{
$cfg = &$this->timelineConfig;
$data = $this->TL_attributesOnTime();
foreach($data as $attr=>$val)
$cfg["attributes"][$attr] = $val;
$cfg["_oldAttributes"] = $cfg["attributes"];
}
private function TL_attributesOnTime()
{
$cfg = &$this->timelineConfig;
$timelineTable = $cfg["TimelineClass"]::tableName();
$sql = "SELECT t1.* FROM $timelineTable AS t1
LEFT JOIN (SELECT * FROM $timelineTable WHERE `table`=:table AND table_id=:table_id AND datetime<=:datetime) AS t2
ON (t1.table=t2.table and t1.table_id=t2.table_id and t1.datetime<t2.datetime AND t1.attribute=t2.attribute)
WHERE t2.id IS NULL AND t1.datetime<:datetime AND t1.table=:table AND t1.table_id=:table_id
";
$params = [
'table'=>$cfg["table"],
'table_id'=>$this->id,
':datetime'=>$cfg["datetime"],
];
$data = \Yii::$app->db->createCommand($sql,$params)->queryAll();
$data = ArrayHelper::map($data,'attribute','value');
return $data;
}
public function TL_EventAFterDelete($event)
{
$cfg = &$this->timelineConfig;
$cfg["TimelineClass"]::deleteAll([
'table'=>$cfg["table"],
'table_id'=>$event->sender->id
]);
}
}
Example of it's use:
<?php
namespace app\models;
class KeyTime extends Key
{
use \app\behaviors\TimelineTrait;
public function init()
{
parent::init();
$this->timelineInit([
'attributes'=>[
// default values for attributes
'keyid'=>'historic id', // this is existing attribute in Key model
'label'=>'mylabel', // label and color does not exist in Key model
'color'=>'red',
],
]);
}
}
The actionUpdate
public function actionUpdate($id)
{
$model = $this->findModel($id);
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['view', 'id' => $model->id]);
}
return $this->render('update', [
'model' => $model,
]);
}
After many "flashes" with microtime(true) on, I found the reason it worked sometimes with sleep(1).
The answer is in TL_attributesOnTime. the last line in $sql was
WHERE t2.id IS NULL AND t1.datetime<:datetime AND t1.table=:table AND t1.table_id=:table_id
…but it should be…
WHERE t2.id IS NULL AND t1.datetime<=:datetime AND t1.table=:table AND t1.table_id=:table_id
Notice the < is changed to <= Otherwise when the record was saved in the same second as it was populated it would not be included.
Hope it can help somebody else.

Routing to controller with optional parameters

I'd like to create a route that takes a required ID, and optional start and end dates ('Ymd'). If dates are omitted, they fall back to a default. (Say last 30 days) and call a controller....lets say 'path#index'
Route::get('/path/{id}/{start?}/{end?}', function($id, $start=null, $end=null)
{
if(!$start)
{
//set start
}
if(!$end)
{
//set end
}
// What is the syntax that goes here to call 'path#index' with $id, $start, and $end?
});
There is no way to call a controller from a Route:::get closure.
Use:
Route::get('/path/{id}/{start?}/{end?}', 'Controller#index');
and handle the parameters in the controller function:
public function index($id, $start = null, $end = null)
{
if (!$start) {
// set start
}
if (!$end) {
// set end
}
// do other stuff
}
This helped me simplify the optional routes parameters (From Laravel Docs):
Occasionally you may need to specify a route parameter, but make the presence of that route parameter optional. You may do so by placing a ? mark after the parameter name. Make sure to give the route's corresponding variable a default value:
Route::get('user/{name?}', function ($name = null) {
return $name;
});
Route::get('user/{name?}', function ($name = 'John') {
return $name;
});
Or if you have a controller call action in your routes then you could do this:
web.php
Route::get('user/{name?}', 'UsersController#index')->name('user.index');
userscontroller.php
public function index($name = 'John') {
// Do something here
}
I hope this helps someone simplify the optional parameters as it did me!
Laravel 5.6 Routing Parameters - Optional parameters
I would handle it with three paths:
Route::get('/path/{id}/{start}/{end}, ...);
Route::get('/path/{id}/{start}, ...);
Route::get('/path/{id}, ...);
Note the order - you want the full path checked first.
Route::get('user/{name?}', function ($name = null) {
return $name;
});
Find more details here (Laravel 7) : https://laravel.com/docs/7.x/routing#parameters-optional-parameters
You can call a controller action from a route closure like this:
Route::get('{slug}', function ($slug, Request $request) {
$app = app();
$locale = $app->getLocale();
// search for an offer with the given slug
$offer = \App\Offer::whereTranslation('slug', $slug, $locale)->first();
if($offer) {
$controller = $app->make(\App\Http\Controllers\OfferController::class);
return $controller->callAction('show', [$offer, $campaign = NULL]);
} else {
// if no offer is found, search for a campaign with the given slug
$campaign = \App\Campaign::whereTranslation('slug', $slug, $locale)->first();
if($campaign) {
$controller = $app->make(\App\Http\Controllers\CampaignController::class);
return $controller->callAction('show', [$campaign]);
}
}
throw new \Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
});
What I did was set the optional parameters as query parameters like so:
Example URL:
/getStuff/2019-08-27?type=0&color=red
Route:
Route::get('/getStuff/{date}','Stuff\StuffController#getStuff');
Controller:
public function getStuff($date)
{
// Optional parameters
$type = Input::get("type");
$color = Input::get("color");
}
Solution to your problem without much changes
Route::get('/path/{id}/{start?}/{end?}', function($id, $start=null, $end=null)
{
if(empty($start))
{
$start = Carbon::now()->subDays(30)->format('Y-m-d');
}
if(empty($end))
{
$end = Carbon::now()->subDays(30)->format('Y-m-d');
}
return App\Http\Controllers\HomeController::Path($id,$start,$end);
});
and then
class HomeController extends Controller
{
public static function Path($id, $start, $end)
{
return view('view');
}
}
now the optimal approach is
use App\Http\Controllers\HomeController;
Route::get('/path/{id}/{start?}/{end?}', [HomeController::class, 'Path']);
then
class HomeController extends Controller
{
public function Path(Request $request)
{
if(empty($start))
{
$start = Carbon::now()->subDays(30)->format('Y-m-d');
}
if(empty($end))
{
$end = Carbon::now()->subDays(30)->format('Y-m-d');
}
//your code
return view('view');
}
}

How to change existing tag information in Magento

I am trying to update the popularity count of Magento's Tag module by interacting with this core function in Mage_Tag_Model_API
public function update($tagId, $data, $store)
{
$data = $this->_prepareDataForUpdate($data);
$storeId = $this->_getStoreId($store);
/** #var $tag Mage_Tag_Model_Tag */
$tag = Mage::getModel('tag/tag')->setStoreId($storeId)->setAddBasePopularity()->load($tagId);
if (!$tag->getId()) {
$this->_fault('tag_not_exists');
}
// store should be set for 'base_popularity' to be saved in Mage_Tag_Model_Resource_Tag::_afterSave()
$tag->setStore($storeId);
if (isset($data['base_popularity'])) {
$tag->setBasePopularity($data['base_popularity']);
}
if (isset($data['name'])) {
$tag->setName(trim($data['name']));
}
if (isset($data['status'])) {
// validate tag status
if (!in_array($data['status'], array(
$tag->getApprovedStatus(), $tag->getPendingStatus(), $tag->getDisabledStatus()))) {
$this->_fault('invalid_data');
}
$tag->setStatus($data['status']);
}
try {
$tag->save();
} catch (Mage_Core_Exception $e) {
$this->_fault('save_error', $e->getMessage());
}
return true;
}
In my controller I have this :
public function clickAction()
{
$tagString = $this->getRequest()->getParam('tag');
$tagByName = Mage::getModel('tag/tag')->loadByName($tagString);
$tagId = $tagByName->getTagId();
$basePopularity = ['base_popularity' => '13']; // hard coding while testing
Mage::getModel('tag/api')->update($tagId, $basePopularity, 1);
}
If I put a log statement in this part of the update function :
try {
// log stuff
$tag->save();
}
I can see it makes it to that try but there is no change in the data. What did I screw up? Any other ideas on how I can update the popularity of a tag through a controller? Using this same method and adding 'name' => 'blah' to that $data array parameter works fine..
I also found in Mage_Tag_Model_Indexer_Summary.php this method defined in the PHPdoc * #method Mage_Tag_Model_Indexer_Summary setPopularity(int $value) Maybe that is what I need... can someone provide an example showing how I could use that magic setter?
Try adding Mage::app()->setCurrentStore(Mage_Core_Model_App::ADMIN_STORE_ID); at the start of your clickAction function. base_popularity can only be updated from admin store.

Mage_Core_Exception with message 'Cannot retrieve entity config: sales/Array'

The following code runs fine under Magento 1.6 but raises a Mage_Core_Exception (message: 'Cannot retrieve entity config: sales/Array') when run under 1.5.0.1. What do I need to do to get this code running under Magento 1.5.0.1?
$results = Mage::getResourceModel('sales/order_collection');
$results->join(
array('status_key_table' => 'order_status'),
'main_table.status = status_key_table.status',
array('status_key_table.label')
);
Thank you,
Ben
If you compare the join() methods between 1.5.0.1 and 1.6.2.0:
1.5.0.1: Mage_Core_Model_Mysql4_Collection_Abstract::join()
public function join($table, $cond, $cols='*')
{
if (!isset($this->_joinedTables[$table])) {
$this->getSelect()->join(array($table=>$this->getTable($table)), $cond, $cols);
$this->_joinedTables[$table] = true;
}
return $this;
}
1.6.2.0: Mage_Core_Model_Resource_Db_Collection_Abstract::join()
public function join($table, $cond, $cols = '*')
{
if (is_array($table)) {
foreach ($table as $k => $v) {
$alias = $k;
$table = $v;
break;
...
You can see that 1.5.0.1 doesn't support aliases. Instead, it calls $this->getTable() on the first parameter you pass in -- which should be a string. So, in your case, you'll need to pass in 'sales/order_status' as the first parameter.

Resources