I am developing an application using Laravel, Eloquent as ORM, phpunit for unit testing. But i am not able to update data in database. Though there is no exception, warning or error. If the object of the model class checked in laravel, before update and after update, it is showing that data has been changed in the model class but when checking the database, found that data is not being changed.
Model Class
class Post extends Model
{
protected $table = "posts";
protected $fillable = [
'id',
'user_id',
'title',
'description',
'total_needed',
'total_collected',
'total_expanse',
'start_date',
'end_date',
'active',
'updated_at',
'created_at',
];
}//class
Repository Code
class Post_Repo_Impl implements Post_Repo_I
{
public function update(Post $postUpdate)
{
$raedOld = false;
$updateStatus = false;
try {
$psot_id = $postUpdate->id;
$postOrgin = Post::find($psot_id);
$raedOld = true;
} catch (Exception $e) {
error_log("Post Update : failed to read existig post.");
}
if ($raedOld) {
try {
//line 1
echo "\n" . $postOrgin->title . "\n";
$this->setPostValues($postOrgin, $postUpdate)->update();
//line 2
echo "\n" . $postOrgin->title . "\n";
$updateStatus = true;
} catch (Exception $e) {
error_log("Post Update : Failed to save updated post." . "\n\n" . $e);
}
}
return $updateStatus;
} //update
}
Line 1 and Line 2, not printing same value. Line 2 printing the changed value.
Unit Test code
class RepoPost extends TestCase
{
public function testMain()
{
echo "\n >----------- Test Main : ---------> \n";
$this->postUpdate();
} //mother test
public function postUpdate()
{
$postDummyUpdate = new Post();
$postDummyUpdate->id = '2';
$postDummyUpdate->user_id = 'Tst';
$postDummyUpdate->title = 'Post Updated Repo Test........';
$postDummyUpdate->description = 'UnitTesting of URLs';
$postDummyUpdate->total_needed = '2000';
$postDummyUpdate->total_collected = '1000';
$postDummyUpdate->total_expanse = '500';
$postDummyUpdate->start_date = '22-09-2019';
$postDummyUpdate->end_date = '22-10-2019';
$postDummyUpdate->active = '1';
$postDummyUpdate->updated_at = '2019-09-24';
$postDummyUpdate->created_at = '2019-09-22';
echo '\n----PostUpdate----\n';
$postRepoSave = $this->getRepoPostImpl();
dd($postRepoSave->update($postDummyUpdate));
if ($postRepoSave == false) {
error_log("\n\nTest : Data Save Failed.");
} else {
error_log("Saved Post ID : " . $postRepoSave);
}
}
public function getRepoPostImpl()
{
return new Post_Repo_Impl();
}
}
In the test code it is returning true.
Related
I am trying to setup google indexing api in codeigniter, I have done all steps on google cloud and search console part.
It works, but returning success message on all options event when url is not submited, that is why I want to get exact response from google instead of a created success message.
How can I display exact response from google return $stringBody;? or check for the correct response ?
Here is my controller :
namespace App\Controllers;
use App\Models\LanguageModel;
use App\Models\IndexingModel;
class IndexingController extends BaseController
{
public function initController(\CodeIgniter\HTTP\RequestInterface $request, \CodeIgniter\HTTP\ResponseInterface $response, \Psr\Log\LoggerInterface $logger)
{
parent::initController($request, $response, $logger);
$this->indexingModel = new IndexingModel();
}
public function GoogleUrl()
{
checkPermission('indexing_api');
$data['title'] = trans("indexing_api");
$data["selectedLangId"] = inputGet('lang');
if (empty($data["selectedLangId"])) {
$data["selectedLangId"] = $this->activeLang->id;
}
echo view('admin/includes/_header', $data);
echo view('admin/indexing_api', $data);
echo view('admin/includes/_footer');
}
/**
* indexing Tools Post
*/
public function indexingToolsPost()
{
checkPermission('indexing_api');
$slug = inputPost('slug');
$urltype = inputPost('urltype');
$val = \Config\Services::validation();
$val->setRule('slug', trans("slug"), 'required|max_length[500]');
if (!$this->validate(getValRules($val))) {
$this->session->setFlashdata('errors', $val->getErrors());
return redirect()->to(adminUrl('indexing_api?slug=' . cleanStr($slug)))->withInput();
} else {
$this->indexingModel->AddUrlToGoogle($slug, $urltype);
$this->session->setFlashdata('success', trans("msg_added"));
resetCacheDataOnChange();
return redirect()->to(adminUrl('indexing_api?slug=' . cleanStr($slug)));
}
$this->session->setFlashdata('error', trans("msg_error"));
return redirect()->to(adminUrl('indexing_api?slug=' . cleanStr($slug)))->withInput();
}
}
And This is my model :
namespace App\Models;
use CodeIgniter\Model;
use Google_Client;
class IndexingModel extends BaseModel {
public function AddUrlToGoogle($google_url, $Urltype){
require_once APPPATH . 'ThirdParty/google-api-php-client/vendor/autoload.php';
$client = new Google_Client();
$client->setAuthConfig(APPPATH . 'ThirdParty/google-api-php-client/xxxxxxxxx.json');
$client->addScope('https://www.googleapis.com/auth/indexing');
$httpClient = $client->authorize();
$endpoint = 'https://indexing.googleapis.com/v3/urlNotifications:publish';
$array = ['url' => $google_url, 'type' => $Urltype];
$content = json_encode($array);
$response = $httpClient->post($endpoint,['body' => $content]);
$body = $response->getBody();
$stringBody = (string)$body;
return $stringBody;
}
public function AddUrlToBing($google_url, $Urltype){
}
public function AddUrlToYandex($google_url, $Urltype){
}
}
This is a success response when I try it out of codeigniter and print_r($stringBody);
{ "urlNotificationMetadata": { "url": "https://example.com/some-text", "latestUpdate": { "url": "https://example.com/some-text", "type": "URL_UPDATED", "notifyTime": "2023-01-29T01:51:13.140372319Z" } } }
And this is an error response :
{ "error": { "code": 400, "message": "Unknown notification type. 'type' attribute is required.", "status": "INVALID_ARGUMENT" } }
But In codeigniter I get a text message "url submited" even if url not submited.
Currently you are not handling the actual response of IndexingModel->AddUrlToGoogle(). It seems your code has a validation before, so it claims, if no validation error occurs, its always a success.
So the first question to ask is, why your validation is not working here - or is it?
Secondly you could handle the actual response in any case:
IndexingController
class IndexingController extends BaseController
public function indexingToolsPost()
{
if (!$this->validate(getValRules($val))) {
// validation error
$this->session->setFlashdata('errors', $val->getErrors());
return redirect()->to(adminUrl('indexing_api?slug=' . cleanStr($slug)))->withInput();
} else {
// no validation error
$apiResponseBody = $this->indexingModel->AddUrlToGoogle($slug, $urltype);
if(array_key_exists('error', $apiResponseBody)) {
// its an error!
// either set the actual messsage
$this->session->setFlashdata('error', $apiResponseBody['error']['message']);
// OR translate it
$this->session->setFlashdata('error', trans($apiResponseBody['error']['message']));
} else {
// Its a success!
$this->session->setFlashdata('success', trans("msg_added"));
}
// ...
}
return redirect()->to(adminUrl('indexing_api?slug=' . cleanStr($slug)))->withInput();
}
And in the model, return the response as an array:
IndexingModel
public function AddUrlToGoogle($google_url, $Urltype) {
// ...
$response = $httpClient->post($endpoint,['body' => $content]);
return json_decode($response->getBody() ?? '', true); // return an array
}
I am building a blog using Laravel 9 and my update method for some unknown reason fails to update
My code Samples
Model
class Anime extends Model
{
use HasFactory;
protected $table = 'anime';
protected $primaryKey = 'id';
protected $fillable = ['anime_title','user_id','blog_title','description','slug','anime_image_profile'];
public function blogInformation() {
return $this->hasMany(BlogInfo::class);
}
public function getRouteKeyName()
{
return 'slug';
}
// protected $hidden = 'id';
}
Controller
public function update(ValidateAnimeBlogRequest $request, $id)
{
$request->validated();
/*Update the details in the database by ID*/
$update_data = Anime::findOrFail($id);
$update_data = new Anime;
$update_data->anime_title = $request->input('anime_title');
$update_data->blog_title = $request->input('blog_title');
$update_data->user_id = auth()->user()->id;
$update_data->description = $request->input('description');
$update_data->slug = Str::slug($request->input('blog_title'));
/*Check if the user also wanted to update the image*/
if($request->hasFile('anime_image_profile')) {
$path_to_images = 'images/anime_image_profile/' . $update_data->anime_image_profile;
if(File::exists($path_to_images)) {
File::delete($path_to_images);
}
$new_file_name = '9anime' . '-' . time() . '-' . $request->name . '.' . $request->anime_image_profile->extension();
$request->anime_image_profile->move(public_path('images/anime_image_profile'), $new_file_name);
$update_data->anime_image_profile = $new_file_name;
}
if($update_data->update()) {
redirect('/');
}
dd('Error');
}
ValidateAnimeBlogRequest
public function rules()
{
return [
'anime_title' => 'required | min:2', new nameRegex,
'blog_title' => ['required','min:5', new nameRegex],
'description' => ['required','min:1000'],
'premiered' => ['required'],
'genre' => ['required', new nameRegex],
'licensors' => ['required', new nameRegex],
'studio' => ['required', new nameRegex],
'anime_image_profile' => 'required | mimes:jpeg,jpg,png | max:5408'
];
}
My blade file
<form enctype="multipart/form-data" autocomplete="off" action="/blog/{{$anime['id']}}" method="POST">
#method('PUT')
#csrf
I set up a custom check just in case
if($update_data->update()) {
redirect('/');
}
dd('Error');
The output on my webpage from this is "Error" // app\Http\Controllers\AnimeController.php:156
And when I dd($update_data) I see that the data has been updated yet it does not get sent to the database.
I tried replacing $update_data->update() with $update_data->save() but that now creates new data in the DB instead of updating the existing one
You can keep it as the save() method. Just update the lines above where you are creating a new Anime() instance to only be created if the record cannot be found via $id from the line above.
public function update(ValidateAnimeBlogRequest $request, $id)
{
$request->validated();
/*Update the details in the database by ID*/
$update_data = Anime::findOrFail($id);
if(!$update_data) {
$update_data = new Anime;
}
$update_data->anime_title = $request->input('anime_title');
$update_data->blog_title = $request->input('blog_title');
$update_data->user_id = auth()->user()->id;
$update_data->description = $request->input('description');
$update_data->slug = Str::slug($request->input('blog_title'));
/*Check if the user also wanted to update the image*/
if($request->hasFile('anime_image_profile')) {
$path_to_images = 'images/anime_image_profile/' . $update_data->anime_image_profile;
if(File::exists($path_to_images)) {
File::delete($path_to_images);
}
$new_file_name = '9anime' . '-' . time() . '-' . $request->name . '.' . $request->anime_image_profile->extension();
$request->anime_image_profile->move(public_path('images/anime_image_profile'), $new_file_name);
$update_data->anime_image_profile = $new_file_name;
}
if($update_data->save()) {
redirect('/');
}
dd('Error');
}
This will create a new instance only if a record is not found and won't give a new db record
I have 'sendsms' function which i used it in one of my controllers and worked fine. now what i need to know how i can make class reference of this code to use it in other controllers, instead of copy/paste whole code in all controllers.
In other Q/A they mentioned about only creating reference but i wanted to do it properly like using constructor or etc, not just doing things work, i want to do it like real-world project.
Here's the code in controller :
public function store(Request $request)
{
$this->validate($request,[
'title' => 'required|string|min:6',
'gametype' => 'required|string|min:3',
'description' => 'required|string|min:1|max:180',
'price' => 'required|numeric|min:4',
'buyyer_id' => 'required|numeric|min:1'
// 'seller_id' => 'required|numeric|min:1'
]);
// return RequestModel::create([
// 'title' => $request['title'],
// 'description' => $request['description'],
// 'gametype' => $request['gametype'],
// 'price' => $request['price'],
// 'buyyer_id' => $request['buyyer_id'],
// 'seller_id' => Auth::user()->id,
// ]);
//
$requestModel = new RequestModel;
// store
$requestModel->title = $request['title'];
$requestModel->description = $request['description'];
$requestModel->gametype = $request['gametype'];
$requestModel->price = $request['price'];
$requestModel->buyyer_id = $request['buyyer_id'];
$requestModel->seller_id = Auth::user()->id;
$requestModel->save();
return $this->sendSms($request['title'], $request['gametype']);
}
// I want to use this code in another class to use it in all controllers without copy/paste it.
function sendSms($reqid, $recgametype) {
//Send sms to getway
//implement later.
$otp_prefix = ':';
$response_type = 'json';
$textMSGATLAS = iconv("UTF-8", 'UTF-8//TRANSLIT',"req : ( " .$reqid. " ) for ( " .$recgametype. " ) submitted ");
ini_set("soap.wsdl_cache_enabled", "0");
try {
$client = new SoapClient("http://xxxx");
$user = "user";
$pass = "pass";
$fromNum = "+xxx";
$toNum = "+xxxx";
$messageContent = $textMSGATLAS;
$op = "send";
$client->SendSMS($fromNum,$toNum,$messageContent,$user,$pass,$op);
} catch (SoapFault $ex) {
echo $ex->faultstring;
}
}
I'm right now learning and I'm beginner at this so help to understand how to make it work properly. Thanks.
You can create a separate SMS class like :
<?php
namespace App;
class SMS {
private $reqid;
private $recgametype;
public function __construct($reqid, $recgametype)
{
$this->reqid = $reqid;
$this->recgametype = $recgametype;
}
public function send()
{
$otp_prefix = ':';
$response_type = 'json';
$textMSGATLAS = iconv("UTF-8", 'UTF-8//TRANSLIT',"req : ( " .$this->reqid. " ) for ( " .$this->recgametype. " ) submitted ");
ini_set("soap.wsdl_cache_enabled", "0");
try {
$client = new SoapClient("http://xxxx");
$user = "user";
$pass = "pass";
$fromNum = "+xxx";
$toNum = "+xxxx";
$messageContent = $textMSGATLAS;
$op = "send";
return $client->SendSMS($fromNum,$toNum,$messageContent,$user,$pass,$op);
} catch (SoapFault $ex) {
throw new \Exception('SMS sending failed')
}
}
}
And then inside controller or wherever you would need :
public function sendSms($reqid, $recgametype) {
$sms = new \App\SMS($reqid, $recgametype);
$sms->send();
}
You can also create custom exception like SMSSendingFailedException and throw it instead of standard \Exception inside send() function.
That will help you to send appropriate response in controller like :
public function sendSms($reqid, $recgametype) {
try{
$sms = new \App\SMS($reqid, $recgametype);
$sms->send();
return response()->json('message' => 'SMS sent successfully', 200);
}
catch(SMSSendingFailedException $e){
return response()->json('message' => 'SMS sending failed', 500);
}
}
Then to go one step further, you can use concept of laravel facade if you need it all over the project with a quick class alias.
I have table USR and ORGANIZATION :
USR table
|ID | ID_APP | NAMA_APP|
ORGANIZATION table
|ID | NAMA |
I tried to insert data to USR table (ID_APP and NAMA_APP) from ORGANIZATION by ID (relationship). This my code :
UsersController :
public function actionUpdate($id)
{
$model = $this->findModel($id);
if (Yii::$app->request->post()) {
try {
$state = true;
$data = Yii::$app->request->post();
$transaction = Yii::$app->db->beginTransaction();
$model->ID_APP = $data['USR']['ID_APP'];
$model->NAMA_APP = $data['USR']['NAMA_APP'];
if (!$model->save()) {
$ErrorMessage = $model->getErrorMessage($model->getErrors());
throw new Exception($ErrorMessage);
}
$transaction->commit();
$message = "Success update Application ";
} catch (Exception $e) {
$state = false;
$transaction->rollback();
$message = $e->getMessage();
}
if ($state) {
Yii::$app->session->setFlash('successApplication', $message);
return $this->redirect(['view', 'id' => $model->ID]);
} else {
Yii::$app->session->setFlash('errorApplication', $message);
return $this->render('view', ['id' => $model->ID]);
}
} else {
return $this->render('update', [
'model' => $model,
]);
}
}
My View :
<?php $org = \app\models\ORGANIZATION::find()->all(); ?>
<?= $form->field($model, 'ID_APP')->dropDownList(
ArrayHelper::map($org,'ID', 'NAMA'))->label('ID APP') ?>
I'm still beginner on php and yii2 framework How to get NAMA_APP (USR table) from NAMA (ORGANIZATION)?
Based on you view assumuning that you post the ID_APP and need to retrive the NAMA_APP for a separated store in USR
// make accessible your ORGANIZAZTION model with use eg:
// use common\models\ORGANIZATION; or
use yourapp\models\ORGANIZATION;
public function actionUpdate($id)
{
$model = $this->findModel($id);
if (Yii::$app->request->post()) {
try {
$state = true;
$data = Yii::$app->request->post();
$transaction = Yii::$app->db->beginTransaction();
$model->ID_APP = $data['USR']['ID_APP'];
// then you can use eg: the find method
// related to ORGANIZAZTION active record
$modelOrg = ORGANIZATION::find()->where(['ID_APP']=> $data['USR']['ID_APP'])->one();
$model->NAMA_APP = $modelOrg->NAMA_APP
....
While saving a user i am updating the state of a record in my preference table from beforesave() of user table by calling a licensingObject() method of my SbLicensingbehavior.
Now if I return true from the beforsave() function then preference table record gets updated.
If I return false then the preference table record is not updated in data base.
user model code:
public function behaviors()
{
return array(
'behaviour_model_download' => array(
'class' => 'application.components.SbLicensingBehavior'
)
);
}
public function beforeSave()
{
$error = $this->licensingObject('user_count','save');
if($error){
return true;
}
return true;
}
Licensing behavior code:
<?php
class SbLicensingBehavior extends CBehavior
{
/**
* This function will receive the variable as parameter who's current state need to be
* incremented or decremented based on operation parameter.
* value send as parameter will be used as amount by which we need to increment our
* current state variable, it will be usefull incase like we have a limitation on size
* of a repo.
*/
public function updateCurrentState($variable,$operation,$value = null)
{
$preferenceMode = Preference::model()->findByAttributes(array(
'variable' => $variable,
'type' => 'system_limit',
));
if(!$preferenceMode){
return 'not found';
}
$currentStateVariable = "current_state_".$variable;
$currentStatePreferenceModel = Preference::model()->findByAttributes(array(
'variable' => $currentStateVariable,
'type' => 'system_limit'
));
if ($operation == 'save'){
$currentStatePreferenceModel->value += ($value == null?1:$value);
if($preferenceMode->value < $currentStatePreferenceModel->value){
$error = $this->updateFlagState($variable,1);
return $error;
}
}
if(!$currentStatePreferenceModel->save()){
return 'Licensing variable can not be updated';
}
return $error;
}
/**
* This function updates the notification variable value.
*/
public function updateFlagState($variable,$value)
{
$prefrenceNotificationModel = Preference::model()->findByAttributes(array(
'variable' => 'notification_'.$variable,
'type' => 'system_limit'
));
if(!$prefrenceNotificationModel){
return 'Licensing variable can not be updated';
}
$prefrenceNotificationModel->value = $value;
$prefrenceNotificationModel->updated = time();
if(!$prefrenceNotificationModel->save()) {
return 'Licensing variable can not be updated';
}
return 'done';
}
public function licensingObject($variable,$operation=null,$value=null)
{
switch ($variable) {
case "user_count":
$error = $this->updateCurrentState($variable,$operation,$value);
return $error;
if($error == 'done'){
return "user count has exceded the licensing limit, user can not be created";
}
break;
default:
}
}
}
I am not getting what i am doing wrong.