fat models and thin controllers in codeigniter - mysql

this is a user.php controller
public function verifyLogin() {
if (isset($_POST["email"])) {
$e = $this->input->post("email");
$p = $this->input->post("pass");
$this->form_validation->set_rules("email", "email", "required|valid_email|xss_clean");
$this->form_validation->set_rules("pass", "password", "required|xss_clean");
if ($this->form_validation->run()) {
$data = array(
'select' => '*',
'table' => 'users',
'where' => "email = '$e' AND activated = '1'"
);
$checklogin = $this->query2->selectData($data);
if ($checklogin === FALSE) {
echo "quering userInfo fails. email is wrong or activation not done";
exit();
} else {
foreach ($checklogin as $row) {
$dbid = $row->id;
$dbusername = $row->username;
$dbpassword = $row->password;
$dbemail = $row->email;
if ($p === $dbpassword) {
$login_data = array(
'name' => $dbusername,
'email' => $dbemail,
'password' => $dbpassword,
'id' => $dbid,
'expire' => '86500',
'secure' => TRUE,
'logged_in' => TRUE
);
$this->input->set_cookie($login_data);
$this->session->set_userdata($login_data);
if ($this->session->userdata("logged_in")) {
$time = time();
$now = unix_to_human($time, TRUE, 'us');
$updateLogin = $this->query1->updateLogin($e, $now);
if ($updateLogin) {
echo "success";
} else {
echo 'update failed';
}
} else {
echo "session failed";
}
}else{
echo 'password incorrect';
}
}
}
} else {
echo "form validation fails";
}
} else {
$this->load->view('header');
$this->load->view('login');
$this->load->view('modal');
$this->load->view('footer');
}
}
this is model.php
public function selectData($data){
if(isset($data['direction'])){
$dir = $data['direction'];
}else{
$dir = "ASC";
}
if(isset($data['offset'])){
$off = $data['offset'];
}else{
$off = '0';
}
if(isset($data['select']) && isset($data['table'])){
$this->db->select($data['select'])->from($data['table']);
}
if(isset($data['where'])){
$this->db->where($data['where']);
}
if(isset($data['order_by_name'])){
$this->db->order_by($data['order_by_name'], $dir);
}
if(isset($data['limit'])){
$this->db->limit($data['limit'], $off);
}
$query = $this->db->get();
if($query){
$d = $query->result();
return $d;
}else{
return FALSE;
}
}
is this a good way of quering database?
i am new to mvc and i am reading everywhere about "fat models and this controllers"
what can be done to make it a good mvc architecture?

its only acceptable to echo out from the controller when you are developing:
if ($checklogin === FALSE) {
echo "quering userInfo fails.
if checking login is false then either show a view or go to a new method like
if ($checklogin === FALSE) {
$this->showLoginFailed($errorMessage) ;
the check login code in the controller is a great example of something that could be refactored to a model. then if you need to check login from another controller its much easier. putting the form validation code in a model would be another choice. often times when you are validating form code you are also inserting/updating to a database table -- so having all those details together in a model can make things easier long term.
"fat model" does not mean one method in a model that does a hundred things. it means the controller says -- did this customer form validate and insert to the database? yes or no? 3 lines of code.
the model has the code that is looking into the "fat" details of the form, validation, database, etc etc. say 50 or more lines compared to the 3 in the controller. but the methods in the model should still be clean: small and specific.

Related

How do I write a single function in laravel that will be usable for different controllers or schedule commands and access different facades of models

I have this public function below but I will have to write a similar code in about 60 other places, I don't want to repeat myself rather, I want to be able to write a single function such that all I need change is 'Dailysaving::', and '00:00:00' each time I use the function. And please note that I will be creating several other schedule commands which this function should work for. How do I go about this please and where am I supposed to place the function I write And how do I access different models from the function. Thanks in advance for anyone that will help me out.
public function handle()
{
$users= Dailysaving::where('debit_time', '00:00:00')->where('status', 'Active')->get();
//die($users);
foreach ($users as $user) {
$email = $user->email;
$amount = $user->amount_daily * 100;
//Let's know where the payment is on the db
$user_id = $user->user_id;
$savings_id = $user->id;
$auth_code= $user->authorization_code;
//
$metastring = '{"custom_fields":[{"user_id":'. $user_id. '}, {"action": "activatedaily"},{"savings_id": '.$savings_id.'},{"savingstype": "dailysavings"}]}';
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => "https://api.paystack.co/transaction/charge_authorization",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'amount'=>$amount,
'email'=>$email,
'authorization_code' =>$auth_code,
'metadata' => $metastring,
]),
CURLOPT_HTTPHEADER => [
"authorization:Bearer sk_test_656456h454545",
"content-type: application/json",
"cache-control: no-cache"
],
));
$response = curl_exec($curl);
$err = curl_error($curl);
if($err){
$failedtranx = new Failedtransaction;
$failedtranx->error = $err;
$failedtranx->save();
}
if($response) {
$tranx = json_decode($response);
if (!$tranx->status) {
// there was an error contacting the Paystack API
//register in failed transaction table
$failedtranx = new Failedtransaction;
$failedtranx->error = $err;
$failedtranx->save();
}
if ('success' == $tranx->data->status) {
$auth_code = $tranx->data->authorization->authorization_code;
$amount = ($tranx->data->amount) / 100;
$last_transaction = $tranx->data->transaction_date;
$payment_ref = $tranx->data->reference;
$record = new Transactionrecord;
$record->payment_ref = $payment_ref;
$record->save();
//saving complete
//die('saved');
$item = Dailysaving::find($savings_id);
$total_deposit = $item->total_deposit + $amount;
$item->total_deposit = $total_deposit;
$item->last_transaction_date = date('Y-m-d H:i:s');
$target = $item->day_target;
if ($target == $total_deposit) {
$item->status = 'Completed';
}
$item->save();
}
echo 'done';
}
else{
echo 'failed';
}
}
}
As I understand you are trying to make custom helper functions.
So you need create helpers.php with your functions and follow instructions in the below answer: https://stackoverflow.com/a/28290359/5407558

Save into multiple tables using stored procedure

I have 2 models: ReceivedGoodsDetail and StockInventory.
When model ReceivedGoodsDetail do actionCreate then StockInventory automatically also will be inserted into table StockInventory.
I use this stored procedure, this what I've tried:
public function actionCreate($id) {
$model = new ReceivedGoodsDetail();
$connection = \Yii::$app->db;
$transaction = $connection->beginTransaction();
$model->ID_Received_Goods = $id;
if ($model->load(Yii::$app->request->post()) && $model->validate()) {
$connection = Yii::$app->db;
$command = $connection->createCommand('{call usp_T_Received_Goods_Detail#InsertData(:ID_Received_Goods,:ID_Item, :Qty, :User)}');
$ID_Received_Goods = $model->ID_Received_Goods;
$ID_Item = $model->ID_Item;
$Qty = $model->Qty;
$User = Yii::$app->user->identity->username;
$command->bindParam(":ID_Received_Goods",$ID_Received_Goods,PDO::PARAM_STR);
$command->bindParam(":ID_Item", $ID_Item, PDO::PARAM_STR);
$command->bindParam(":Qty", $Qty, PDO::PARAM_INT);
$command->bindParam(":User", $User, PDO::PARAM_STR);
if ($command->execute() == 0) {
$transaction->commit();
} else {
$transaction->rollBack();
foreach ($model->getErrors() as $key => $message) {
Yii::$app->session->setFlash('error', $message);
}
}
return $this->redirect(['receivedgoodsheader/view', 'id' => $model->ID_Received_Goods]);
} else {
return $this->render('create', [
'model' => $model,
]);
}
}
But I'm confused if use 2 models like the case above
Do not be afraid to do such things, here is nothing bad to use stored procedures. But in general your code is not clean and pretty confusing.
First of all, if you are using stored procedure, then why do not make a trigger for ReceivedGoodsDetail (on INSTERT)? IMHO everything will be much simpler with trigger.
Here are some remarks for your implementation.
Why do you open transaction before first if? If validation fails then your transaction will not be closed
manually.
I cant't see here using of 2 models, just one - ReceivedGoodsDetail, and StockInventory
as i can understand will be created in stored procedure usp_T_Received_Goods_Detail#InsertData?
Why do you redirect user to the item view even if transaction fails?
Using ActiveRecord. Then here is no need to start transaction manually. Just define what operations
you wish to be transactional for this model in transactions() method.
Using ActiveRecord. It is better practice to get db connection from your model class, not application.
It will be Yii::$app->db by default, but later you can easily change connection for this particular model.
It will be better for you (for example) to extend ActiveRecord (if not yet) and overload insertInternal() method
for ReceivedGoodsDetail.
In class ReceivedGoodsDetail:
public function transactions() {
return [
'default' => self::OP_INSERT
];
}
protected function insertInternal($attributeNames = null) {
if (!$this->beforeSave(true)) {
return false;
}
$values = $this->getDirtyAttributes($attributes);
/* overrided part of code */
$connection = static::getDb();
$command = $connection->createCommand('{call usp_T_Received_Goods_Detail#InsertData(:ID_Received_Goods,:ID_Item, :Qty, :User)}');
$ID_Received_Goods = $model->ID_Received_Goods;
$ID_Item = $model->ID_Item;
$Qty = $model->Qty;
$User = Yii::$app->user->identity->username;
$command->bindParam(":ID_Received_Goods",$ID_Received_Goods,PDO::PARAM_STR);
$command->bindParam(":ID_Item", $ID_Item, PDO::PARAM_STR);
$command->bindParam(":Qty", $Qty, PDO::PARAM_INT);
$command->bindParam(":User", $User, PDO::PARAM_STR);
if($command->execute() != 0) {
return false;
}
/* end of overrided part */
$changedAttributes = array_fill_keys(array_keys($values), null);
$this->setOldAttributes($values);
$this->afterSave(true, $changedAttributes);
return true;
}
In your action:
public function actionCreate($id) {
$model = new ReceivedGoodsDetail();
$model->ID_Received_Goods = $id;
if ($model->load(Yii::$app->request->post()) && $model->save(true)) {
return $this->redirect(['receivedgoodsheader/view', 'id' => $model->ID_Received_Goods]);
} else {
foreach ($model->getErrors() as $key => $message) {
Yii::$app->session->setFlash('error', $message);
}
return $this->render('create', [
'model' => $model,
]);
}
}
And then catch your flash messages on create form.
P.S. One more moment. It is strange practice to use path/to/model/{id} endpoint
with predefined ID to create new instance. Usually this looks like POST path/to/model. But this can be subject of your business logic, so i don't know if it can be improved or not
P.P.S. This example was not tested (obviously) so here can be some mistakes

how do i make session variables available through out all controllers and views in cakephp 3.2

my login function in users controller
public function login() {
if ($this->request->is('post')) {
$user = $this->Auth->identify();
// print_r($user);
// die();
if ($user['role'] === 'student') {
$this->Auth->setUser($user);
$session = $this->request->session();
$session->write('user', $user);
return $this->redirect(['controller' => 'Useracountinfo/addinfo']);
} elseif .....
and my add info function in the Useracountinfo
public function addinfo()
{
$this->loadModel('Users');
$userinfo= $this->Users->find('all');
$session = $this->request->session();
$userinfo = $session->read('user');
//print_r($userinfo);
//die();
$this->set($userinfo);
$user = $this->Useracountinfo->newEntity();
if ($this->request->is('post')) {
$user = $this->Useracountinfo->patchEntity($userinfo, $this->request->data);
print_r($user);
die();
if ($this->Useracountinfo->save($user)) {
$this->Flash->success(__('The user has been saved.'));
return $this->redirect(['action' => 'index']);
} else {
$this->Flash->error(__('The user could not be saved. Please, try again.'));
}
}
$this->set(compact('useracountinfo'));
$this->set('_serialize', ['useracountinfo']);
}`
and in my view i have something like this
<?php echo '<strong>'.$userinfo['email'].'</strong>'; ?>
the correct syntax is (see the manual)
$this->set('variable_name', somevalue);
so in your case
$this->set('userinfo', $userinfo);
mind that this is not a session variable but just a php variable that cake share from the controller to the view
if you want this to be available in every view you can do it in AppController
but you don't even need to do that because $this->request->session() is already always available in views too
This said I don't fully understand what you are trying to achieve so maybe there are better solutions

Cakephp 2.61 and comboboxes

Using Cakephp 2.6.1, I have successfully captured and stored single characters in my database using the following code
echo $this->Form->input('grade', array('options' => array( 'G' => 'Good', 'P' => 'Pass','R'=>'Practice Required','F'=>'Fail')));
What I would like to know is how to convert these values back to the display values when retrieving them from the database, ie if the database contains 'P', I want to display 'Pass' in the view and index pages.
I'm certain the answer is simple and straightforward, and sheepishly apologise in advance for my ignorance.
Step-1
Create two new files under Vendor folder.
master-arrays.php
common-functions.php
and Import this two files
Process of import :
Dir : app\Config\bootstrap.php
Add this two lines:
App::import('Vendor', 'common-functions');
App::import('Vendor', 'master-arrays');
Step-2
Now open 'master-arrays.php'
and add this array
function grade_type()
{
$GRADE_TYPE['G'] = 'Good';
$GRADE_TYPE['P'] = 'Pass';
return $GRADE_TYPE;
}
Step-3
Change your view -
echo $this->Form->input('grade', array('options' =>grade_type())));
Step-4
Now add this function in 'common-functions.php'
function id_to_text($id, $arr_master=array()) {
$txt_selected = "";
$id = ''.$id; //Added this as it was creating some warnings online for wrong parameter types
if(is_array($arr_master) && array_key_exists($id,$arr_master)) {
$txt_selected = $arr_master[$id];
} else {
$txt_selected = "";
}
return $txt_selected;
}
Step-5
In Controller or in view
Input:
id_to_text('P',grade_type());
Output:
Pass
Try this,
// In controller
function fun()
{
$grade = array(
'G' => 'Good',
'P' => 'Pass',
'R' => 'Practice Required',
'F' => 'Fail'
);
$this->set('grade', $grade);
if ($this->request->data) {
$key = $this->request->data['Model']['grade'];
$this->request->data['Model']['grade'] = $grade[$key];
$this->Model->save($this->request->data);
}
}
// in view
echo $this->Form->input('grade', array(
'options' => $grade
));
Updated Code
// add in Model
function afterFind($results) {
foreach ($results as $key => $val) {
if (isset($val['MyModel']['status'])) {
$results[$key]['MyModel']['grade_text'] = $this->getGradeText($results[$key]['MyModel']['grade']);
}
}
return $results;
}
public function getGradeText($key)
{
$grades = array(
'G' => 'Good',
'P' => 'Pass',
'R' => 'Practice Required',
'F' => 'Fail'
);
$txt_selected = "";
if(array_key_exists($key,$grades)) {
$txt_selected = $grades[$key];
} else {
$txt_selected = "";
}
return $txt_selected;
}
// in controller
public function index() {
$this->set('data' , $this->paginate('MyModel'));
}
// in view
foreach($data as $value) {
echo $value['MyModel']['grade_text'];
}
I hope it will be help you.

Laravel error:: Illegal string offset 'category_name'

I'm trying to create an admin side form. Here i'm trying to select data from the database. Also i want to display it. But for some reason it's not working. Here's my controller code,
public function save()
{
if (Input::has('save'))
{
$rules = array('category_name' => 'required|min:1|max:50', 'parent_category' => 'required');
$input = Input::all();
$messages = array('category_name.required' =>'Please enter the category name.', 'category_name.min' => 'Category name must be more than 4 characters', 'category_name.max' =>'Category name must not be more than 15 characters!!!', 'parent_category.required' => 'Please Select Parent Category.',);
$validator = Validator::make($input, $rules, $messages);
if ($validator->fails())
{
return Redirect::to('admin/category/add')->withErrors($validator)->withInput($input);
}
else
{
$insert_db = CategoryModel::insert($input);
$selected_category = CategoryModel::select($input['category_name']);
}
}
}
and my CategoryModel.php is following.
public static function insert($values)
{
$insert = DB::table('add_category')->insert(array('category_name'=>$values['category_name'], 'parent_category'=>$values['parent_category']));
if(isset($insert))
{
echo 'inserted successfully';
}
else
{
echo 'Failed';
}
}
public static function select($values)
{
$insert = DB::table('add_category')->where('category_name' . '=' . $values['category_name']);
}