I'm very new to PHP and Slim Framework which helps creating APIs.
Everything is ok If i query db inside $app->post or get. But I want to separate it to normal function. It will help when I need to use it later in other APIs.
I tried to call this
$app->get('/search/[{phone}]', function($request, $response, $args) use ($app){
$token = $response->getHeader('token');
// $phone = $args['phone'];
if (isTokenValid($token)){
return $this->response->withJson("valid");
}
return $this->response->withJson("invalid");
});
My isTokenValid() function
function isTokenValid($token){
$sql = 'SELECT id FROM users WHERE token = :token';
$s = $app->db->prepare($sql); //<< this line 25
$s->bindParam(':token', $token);
if ($s->execute()){
if($sth->rowCount() > 0){
return true;
}
}
return false;
}
But I get 500 Internal Server Error
Type: Error
Message: Call to a member function prepare() on null
File: /Applications/MAMP/htdocs/aigoido/src/functions.php
Line: 25
How to call it outside $app? Thanks.
You want to create a dependency injection container for your database connection and pass that object in as the function parameter rather than app object. This makes the db connection reusable throughout your app.
https://www.slimframework.com/docs/concepts/di.html
Also, you can return $response rather than $this->response.
$c = $app->getContainer();
$c['db'] = function() {
return new DB($host,$user,$pass,$name);
};
$app->post('/search/[{phone}]', function($request, $response, $args) use ($c) {
$token = $response->getHeader('token');
// $phone = $args['phone'];
if (isTokenValid($c->db,$token)){
return $response->withJson("valid");
}
return $response->withJson("invalid");
});
function isTokenValid($db, $token){
$sql = 'SELECT id FROM users WHERE token = :token';
$s = $db->prepare($sql);
$s->bindParam(':token', $token);
if ($s->execute()){
if($sth->rowCount() > 0){
return true;
}
}
return false;
}
Pass $app to your function as parameter. The function has it own context so $app is not available without that.
function isTokenValid($token, $app){
$sql = 'SELECT id FROM users WHERE token = :token';
$s = $app->db->prepare($sql); //<< this line 25
$s->bindParam(':token', $token);
if ($s->execute()){
if($sth->rowCount() > 0){
return true;
}
}
return false;
}
Related
I'm trying to understand the reason of this error message from postman when test API.
When I am testing my REST API from postman, it gives me error
ErrorException (E_NOTICE)
Trying to get property 'staff' of non-object
I try to find the problem but i can't find it. I kept searching for this but couldn't find an answer that will make this clear.
Anyone can help me on this?
Thanks!
This my code snippet
public function updatestatus($request, $leave, $is_api=0)
{
$status = $request->get('status');
$user = $is_api? JWTAuth::parseToken()->authenticate():Auth::user();
// $user = Auth::user();
$staff= $user->ref_user;
$applying_staff = $leave->staff;
$applying_user = $applying_staff->main_user;
//Approved
if($status==2 && $leave->status==1)
{
$leave->status =2;
$leave->approved_by_staff_id = $staff->staff_id;
$leave->approved_date = new Carbon('today');
$leave->save();
if($user->centre_id)
Helper::ClearObjective(10,$user->centre_id);
dispatch(new EmailJob($applying_user->email,new LeaveNotification(route('staff.leave.show',$leave->id), $leave->statusStr)))
->onConnection('database')
->onQueue('emails');
return response()->json(['Success'=>'Success']);
//send email
}
// Rejected
else if($status==3 && $leave->status==1)
{
$leave->status =3;
$leave->status_rejected_reason = $request->get('reason',null);
$leave->save();
if($user->centre_id)
Helper::ClearObjective(10,$user->centre_id);
$leave_cat = LeaveCategory::find($leave->leave_type);
if($leave_cat->leave_status!=0)
{
$leave_ent = $applying_staff->leaves()->where('leave_type',$leave->leave_type)->first();
if($leave_ent)
{
$leave_ent->leave_remaining += $leave->leave_days;
$leave_ent->save();
}
}
dispatch(new EmailJob($applying_user->email,new LeaveNotification(route('staff.leave.show',$leave->id), $leave->statusStr, $leave->status_rejected_reason)))
->onConnection('database')
->onQueue('emails');
//send email
}
//Cancelled
else if(($status==4 && $leave->status==2) || ($status==4 && $leave->staff_id == $staff->staff_id))
{
$leave->status =4;
$leave->status_rejected_reason = $request->get('reason',null);
$leave->save();
$leave_cat = LeaveCategory::find($leave->leave_type);
if($leave_cat->leave_status!=0)
{
$leave_ent = $applying_staff->leaves()->where('leave_type',$leave->leave_type)->first();
if($leave_ent)
{
$leave_ent->leave_remaining += $leave->leave_days;
$leave_ent->save();
}
}
dispatch(new EmailJob($applying_user->email,new LeaveNotification(route('staff.leave.show',$leave->id), $leave->statusStr,$leave->status_rejected_reason)))
->onConnection('database')
->onQueue('emails');
//send email
}
// return reponse()->toJson(compact('leave'));
return $leave;
}
Calling API
public function update(Request $request)
{
return $this->leaveApplicationRepository->updatestatus($request,1);
}
your updatestatus() need 3 parameter and your update() function pass only 2 paramenter;
public function update(Request $request)
{
// please provide you leave data
$leave = "your data is here";
return $this->leaveApplicationRepository->updatestatus($request,$leave, 1);
}
From the context that I can see, it is trying to reference the staff variable in:
$applying_staff = $leave->staff;
The non-object message will mean that $leave itself is the problem. Since you are passing in 1 to the call:
return $this->leaveApplicationRepository->updatestatus($request,1);
the 1 becomes the $leave parameter, and that is not an object, hence the error.
Maybe you think that by passing in 1, it will find the correct model automatically? This is not the case. You need to load that model explicitly.
I am using the slim framework to create an api which contain a route to get all accounts from my database. I try to return the list of accounts in json but it remove the last two characters which makes it an invalid Json because it is expected to end with }]. I do not know why it is doing that and how to solve it.
<?php
header("Content-Type: application/json;charset=utf-8");
class Account {
public function getAll(){
$db_connection = new Connection();
$conn = $db_connection->getConnection();
$result = $conn->query("SELECT * from accounts");
$numrows = $result->rowCount();
if ($numrows > 0) {
$rowset = $result->fetchAll(PDO::FETCH_ASSOC);
}
else {
$message['Error'] = 'No Account found';
$rowset = $message;
}
return $rowset;
}
}
I am calling the getAll method in my route like
$app->get('/Account/GetAll', function($request, $response, $args) use ($app){
$application = new Account();
return $response->withJSON($application->getAll());
});
You have some whitespace outside of PHP tags. The most likely case is that you have a blank line above a <?php. Alternatively, you may have a two blank lines after a ?>.
I am currently trying to follow a Slim tutorial that is utilizing $app = Slim::getInstance(); I don't know much about Slim, so the solutions to use a container do not make sense to me. What can I do to make my function provided below actually run?
function jsonResponse($data, $code = 200)
{
$app = Slim::getInstance();
$app->response->setStatus($code);
$app->response->headers->set(
'Content-type',
'application/json; charset=utf-8'
);
return $app->response->setBody(json_encode($data));
}
I am calling this inside another function for logging in that looks like this:
function login($request) {
$user = json_decode($request->getBody());
$username = $user->username;
$password = $user->password;
if (empty($username) || empty($password)) {
$error = 'Username and password are required';
// Bad request
return jsonResponse($error, 400);
}
$sql = "SELECT first_name, username FROM users "
. "WHERE username = '$username' AND password = '$password'";
$db = getConnection();
$row = array();
try {
$result = $db->query($sql);
if (!$result) {
$error = 'Invalid query: ' . mysql_error();
// Internal server error
return jsonResponse($error, 500);
}
$user = $result->fetchAll(PDO::FETCH_OBJ);
if (empty($user)) {
// Unauthorized
return jsonResponse($error, 401);
}
$row["user"] = $user;
$db = null;
} catch(PDOException $e) {
error_log('{"error":{"text":'. $e->getMessage() .'}}');
// $error = array( 'error' => array ( 'text' => $e->getMessage() ) );
// Internal server error
return jsonResponse($error, 500);
}
// OK, default is 200
return jsonResponse($row);
}
My route for the login function is $app->post('/login_user', 'login');
tl;dr I would like an explanation on how to convert older Slim code that uses getInstance().
Thank you!
It's actually pretty straightforward. In this particular case you don't need jsonResponse() function at all. Your login controller will need these changes:
function login($request, $response, $args) {
// ... some code ...
if ($isError) {
return $response->withStatus(500)->withJson($error);
}
return $response->withJson($row); // Status=200 is default.
}
In general, as was said in the comments, Slim3 has no static method to get a Singleton instance. If you wanted to hook on the response object in Slim3, the best way would be to create a middleware.
Or, if you really wanted to access $response from external function, you pass it as a function parameter (respecting dependency injection pattern and keeping code testable): jsonResponse($response, $error, 500);.
Technically, $app is a global variable, but I would suggest against accessing it through $GLOBALS.
I'm trying to call a function I created in MySQL using the Slim framework.
This is my function in DBHandler.php:
public function validarSincronismo($pCnpj, $pLogin, $pImei){
$stmt = $this->conn->prepare("SELECT sincronizar(?,?,?)");
$stmt->bind_param("sss", $pCnpj, $pLogin, $pImei);
$result = $stmt->execute();
$stmt->close();
return $result;
}
And this is the function in my index.php:
$app->post('/validar', function() use ($app) {
$db = new DbHandler();
$cnpj = $app->request->post('cnpj');
$login = $app->request->post('login');
$imei = $app->request->post('imei');
$msg = $db->validarSincronismo($cnpj, $login, $imei);
$response["error"] = false;
$response["message"] = $msg;
echoRespnse(201, $response);
});
And I'm getting the following error in phperror.log:
[17-Sep-2015 21:12:37 UTC] PHP Fatal error: Call to a member function execute() on boolean in C:\MAMP\htdocs\test\include\DbHandler.php on line 69
I tried using CALL sincronizar(?,?,?); But it doesn't execute the SQL function.
Thanks #GustavoStraube and #NorbertvanNobelen for taking the time and looking into my question! I was able to call my SQL function using SELECT sincronizar(). The problem was that I had created the function in the wrong database. My bad! :/
So my final and working code looks as follows:
Function in DBHandler.php
public function validarSincronismo($pCnpj, $pLogin, $pImei){
$stmt = $this->conn->prepare("SELECT sincronizar(?,?,?)");
$stmt->bind_param("sss", $pCnpj, $pLogin, $pImei);
$stmt->execute();
$stmt->bind_result($result);
$stmt->fetch();
$stmt->close();
// Returns a message
return $result;
}
Function in index.php
$app->post('/validar', function() use ($app) {
$db = new DbHandler();
$cnpj = $app->request->post('cnpj');
$login = $app->request->post('login');
$imei = $app->request->post('imei');
$msg = $db->validarSincronismo($cnpj, $login, $imei);
$response["error"] = false;
$response["message"] = $msg;
echoResponse(201, $response);
});
I'm trying to test that a duplicate user cannot be inserted. To do this, I am creating 2 user objects with the same details, just changing account name - since this is something that is also unique.
However, my setter doesn't seem to be setting the company name second time around. When I echo from the model, the property I am trying to set is still the same from the previous object I created. My test fails as it throws the account already exists exception I set up
Failed asserting that exception of type "Models\AccountAlreadyExistsException" matches expected exception "Models\UserAlreadyExistsException".
public function testCantInsertDuplicateUser ()
{
$user = new \Models\User();
$user->first_name = 'John';
$user->surname = 'Smith';
$user->email = 'myemail#gmail.com';
$user->password = 'password';
$user->password_confirmation = 'password';
$user->setCompanyName('Star');
$user->setPackageId(2);
$this->assertTrue($user->signUp());
$user2 = new \Models\User();
$user2->first_name = 'John';
$user2->surname = 'Smith';
$user2->email = 'myemail#gmail.com';
$user2->password = 'password';
$user2->password_confirmation = 'password';
$user2->setCompanyName('cross');
$user2->setPackageId(2);
$this->setExpectedException('Models\UserAlreadyExistsException');
$user2->signUp();
}
//user model
public function setCompanyName ($company_name)
{
$this->company_name = $company_name;
}
private function insertAccount ()
{
$account = new \Models\Account;
$account->setCompanyName($this->company_name);
$account->setPackageId($this->package_id);
$this->account_message_bag = new \Illuminate\Support\MessageBag();
if (!$account->insert()) {
$this->account_message_bag = $account->getValidationErrors();
}
return $account;
}
private function insertUser ()
{
$save_user = $this->save(self::$rules, array(), array(), function ($model)
{
//this is all performed before save
$existing_email = User::where('email', "=", $this->email)->count();
if ($existing_email) {
//delete account that was created in previous step
//as the signup hasn't been successful
$this->account->delete();
throw new UserAlreadyExistsException();
}
//are there any account validation errors?
if (count($this->account_message_bag->getMessages()>0)) {
return false;
}
});
return $save_user;
}
public function signUp ()
{
$this->account = $this->insertAccount();
$this->account_id = $this->account->getId();
if (!$this->insertUser()) {
//delete the company created in previous step
$this->account->delete();
$user_message_bag = Ardent::errors();
//combine user and account validation eerrors
$message_array = array_merge($user_message_bag->getMessages(), $this->account_message_bag->getMessages());
$this->validation_errors = new \Illuminate\Support\MessageBag($message_array);
throw new GenericValidationException();
}
//TODO - does this return false on failure?
$sent = $this->sendConfirmEmail();
if (!$sent) {
throw new WelcomeEmailNotSent();
}
//sende confirm email
return true;
}
//Account model
public function insert ()
{
$result = $this->save(self::$rules, array(), array(), function()
{
$existing_company_name = \Models\Account::where('company_name', '=', $this->company_name)->count();
if ($existing_company_name) {
throw new AccountAlreadyExistsException();
}
});
if (!$result) {
$this->validation_errors = Ardent::errors();
return false;
}
return true;
}
check the way you validate the AccountAlreadyExistsException
may be you should change the email
Your namespaces are likely the problem.
You create the class in \Models, but use Models (without the backslash) for the exception. Maybe your code is throwing \Models\UserAlreadyExsitsException, not Models\UserAlreadyExistsException.
$user2 = new \Models\User();
...
$this->setExpectedException('Models\UserAlreadyExistsException');
Maybe this should be '\Models\UserAlreadyExistsException'
$user2 = new \Models\User();
...
$this->setExpectedException('\Models\UserAlreadyExistsException');