Set Request Methods for REST API - php

I'm trying to implement a RESTful API for my web app.
I have learned from some tutorials how to write the classes for REST API service.
However, in one of the tutorials, they use AngularJS to send HTTP.delete requests to the server on button clicks.
If I don't want to use AngularJS, how can I set the request method in the HTML form to delete a specified user?
For example, if I go to localhost/app/api/user/3
I will get the information of User of id 3 in JSON representation because the default request method is GET.
How can I send delete request to localhost/app/api/user/3 and to delete this user?
In HTML forms, there are only POST and GET methods.
I also don't know how the PUT works...
The following are the classes:
<?php
class REST {
public $_request_args = array();
private $_method = "";
public $_content_type = "application/json";
private $_code = 200;
public function __construct() {
$this->inputs();
}
private function inputs() {
$this->_method = $this->get_request_method();
switch($this->_method){
case "POST":
$this->_request_args = $this->cleanInputs($_POST);
break;
case "GET":
$this->_request_args = $this->cleanInputs($_GET);
break;
case "DELETE":
case "PUT":
parse_str(file_get_contents("php://input"),$this->_request_args);
$this->_request_args = $this->cleanInputs($this->_request_args);
break;
default:
$this->response('Method Not Allowed',405);
break;
}
}
private function get_status_message(){
$status = array(
200 => 'OK',
204 => 'No Content',
404 => 'Not Found',
405 => 'Method Not Allowed',
406 => 'Not Acceptable',
500 => 'Internal Server Error');
return ($status[$this->_code]) ? $status[$this->_code] : $status[500];
}
public function get_request_method(){
$request_method = $_SERVER['REQUEST_METHOD'];
if ($request_method == 'POST' && array_key_exists('HTTP_X_HTTP_METHOD', $_SERVER)) {
if ($_SERVER['HTTP_X_HTTP_METHOD'] == 'DELETE') {
$request_method = 'DELETE';
} else if ($_SERVER['HTTP_X_HTTP_METHOD'] == 'PUT') {
$request_method = 'PUT';
} else {
throw new Exception("Unexpected Header");
}
}
return $request_method;
}
private function cleanInputs($data){
$clean_input = array();
if (is_array($data)) {
foreach ($data as $k => $v) {
$clean_input[$k] = $this->cleanInputs($v);
}
} else {
if(get_magic_quotes_gpc()) {
$data = trim(stripslashes($data));
}
$data = strip_tags($data);
$clean_input = trim($data);
}
return $clean_input;
}
private function set_headers() {
header("HTTP/1.1 ".$this->_code." ".$this->get_status_message());
header("Content-Type:".$this->_content_type);
}
public function response($data, $status = 200) {
$this->_code = ($status)? $status : 200;
$this->set_headers();
if (is_array($data))
echo json_encode($data, JSON_PRETTY_PRINT);
else
echo $data;
exit;
}
}
?>
//EDIT::
<?php
class API extends REST {
public $data = "";
public function processApi() {
$request_args = explode('/', rtrim($_SERVER["PATH_INFO"], '/'));
$func = array_shift($request_args);
if(method_exists($this, $func))
$this->$func();
else {
$this->response('',404); // If the method not exist with in this class "Page not found".
}
}
private function users() {
if($this->get_request_method() != "GET"){
$this->response('',405);
}
$conn = DBManager::getConnection();
$stmt = $conn->prepare("SELECT * FROM USER");
$stmt->execute();
$result = $stmt->fetchAll();
$this->response($result);
}
private function courses() {
if($this->get_request_method() != "GET"){
$this->response('',405);
}
$conn = DBManager::getConnection();
$stmt = $conn->prepare("SELECT * FROM COURSE");
$stmt->execute();
$result = $stmt->fetchAll();
$this->response($result);
}
private function subjects() {
if($this->get_request_method() != "GET"){
$this->response('',405);
}
$conn = DBManager::getConnection();
$stmt = $conn->prepare("SELECT * FROM SUBJECT");
$stmt->execute();
$result = $stmt->fetchAll();
$this->response($result);
}
}
$api = new API;
$api->processApi();
?>
So my question is if I have a form like the following:
<form action='api/users/3' method='POST'>
<input type='submit' value='Delete User' />
</form>
How can I actually delete this user with that request even though the method is POST not DELETE?
I have checked other posts and there's no answer to this question!

Browsers do not support HTTP DELETE and PUT methods in HTML Forms. To get support for the DELETE method, you can insert a pseudo "method" (a hidden text element) in your FORM, this is how the Slim and Breeze router / "micro-framework" handles PUT and DELETE methods.
<form action='api/users/3' method='POST'>
<input type="hidden" name="_method" value="PUT | DELETE" />
<input type='submit' value='Delete User' />
</form>
In Breeze, using the DELETE method will look like this in your PHP script:
delete('/', function(){
echo 'you made a DELETE request';
});
You may be able to do something similar. Alternatively, you may consider switching to a REST router or micro-framework that comes with support for DELETE and PUT.

for Google Chrome, you can install a very helpful extension that helps you to develop and test your REST API: Advanced REST Client
Also, you need to specify the HTTP.delete case as the following:
case "DELETE":
parse_str(file_get_contents("php://input"),$this->_request_args);
$this->_request_args = $this->cleanInputs($this->_request_args);
break;
There seems to be something wrong with the processApi() function because that's not how you get the arguments.
Fix your HTACCESS file to redirect URLs properly.
Follow this tutorial for more info: http://coreymaynard.com/blog/creating-a-restful-api-with-php/

Related

Problem with accessing values in Request Session that I put into

I am fairly new to Laravel. This may have an obvious solution but I can't seem to find it so far. Therefore I am asking for help.
Question In Short:
I use Illuminate\Http\Request session ($request->session()) to store some data I get from BigCommerce API. But I can't get them when I need the data.
Context:
I am building a sample app/boilerplate app for BigCommerce platform using Laravel/React. I have built the app using official documentation, semi-official posts released by BigCommerce team and sample codebase provided by them as well.
App works fine with local credentials from a specific store because they are given as environment variables to the app. However I can't read store_hash (which is necessary to fetch data from BigCommerce) and access token. Both I have put in $request->session() object.
I will paste the AppController.php code below, also code is publicly available here:
In the makeBigCommerceAPIRequest method below (as you can see my debugging efforts :)) I can get $this->getAppClientId(), but I can't get anything from $request->session()->get('store_hash') or $request->session()->get('access_token') which returns from $this->getAccessToken($request).
I have tried putting store hash into a global variable, but it didn't work.
From everything I have experienced so far, $request is not working as expected.
Any help appriciated, thanks in advance.
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use mysql_xdevapi\Exception;
use Oseintow\Bigcommerce\Bigcommerce;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use Bigcommerce\Api\Client as BigcommerceClient;
use Illuminate\Support\Facades\Storage;
use App\Config; //Database Connection
use Bigcommerce\Api\Connection;
class AppController extends Controller
{
protected $bigcommerce;
private $client_id;
private $client_secret;
private $access_token;
private $storehash;
private $redirect_uri;
public function __construct(Bigcommerce $bigcommerce)
{
$this->bigcommerce = $bigcommerce;
$this->client_id = \config('app.clientId');
$this->client_secret = \config('app.clientSecret');
$this->redirect_uri = \config('app.authCallback');
}
public function getAppClientId()
{
if (\config('app.appEnv') === 'local') {
return \config('app.localClientId');
} else {
return \config('app.clientId');
}
}
public function getAppSecret()
{
if (\config('app.appEnv') === 'local') {
return \config('app.localClientSecret');
} else {
return \config('app.clientSecret');
}
}
public function getAccessToken(Request $request)
{
if (\config('app.appEnv') === 'local') {
return \config('app.localAccessToken');
} else {
return $request->session()->get('access_token');
}
}
public function getStoreHash(Request $request)
{
if (\config('app.appEnv') === 'local') {
return \config('app.localStoreHash');
} else {
return $request->session()->get('store_hash');
}
}
public function error(Request $request)
{
$errorMessage = "Internal Application Error";
if ($request->session()->has('error_message')) {
$errorMessage = $request->session()->get('error_message');
}
echo '<h4>An issue has occurred:</h4> <p>' . $errorMessage . '</p> Go back to home';
}
public function load(Request $request)
{
$signedPayload = $request->get('signed_payload');
if (!empty($signedPayload)) {
echo "hello";
$verifiedSignedRequestData = $this->verifySignedRequest($signedPayload);
if ($verifiedSignedRequestData !== null) {
echo "positive return";
$request->session()->put('user_id', $verifiedSignedRequestData['user']['id']);
$request->session()->put('user_email', $verifiedSignedRequestData['user']['email']);
$request->session()->put('owner_id', $verifiedSignedRequestData['owner']['id']);
$request->session()->put('owner_email', $verifiedSignedRequestData['owner']['email']);
$request->session()->put('store_hash', $verifiedSignedRequestData['context']);
echo $request->session()->get('store_hash');
$this->storehash = $verifiedSignedRequestData['context'];
echo ' store hash is at the moment : ' . $this->storehash . ' .....';
} else {
return "The signed request from BigCommerce could not be validated.";
// return redirect()->action([AppController::class, 'error'])->with('error_message', 'The signed request from BigCommerce could not be validated.');
}
} else {
return "The signed request from BigCommerce was empty.";
// return redirect()->action([AppController::class, 'error'])->with('error_message', 'The signed request from BigCommerce was empty.');
}
return redirect(\config('app.appUrl'));
}
public function install(Request $request)
{
// Make sure all required query params have been passed
if (!$request->has('code') || !$request->has('scope') || !$request->has('context')) {
echo 'Not enough information was passed to install this app.';
// return redirect()->action('MainController#error')->with('error_message', 'Not enough information was passed to install this app.');
}
try {
$client = new Client();
$result = $client->request('POST', 'https://login.bigcommerce.com/oauth2/token', [
'json' => [
'client_id' => $this->client_id,
'client_secret' => $this->client_secret,
'redirect_uri' => $this->redirect_uri,
'grant_type' => 'authorization_code',
'code' => $request->input('code'),
'scope' => $request->input('scope'),
'context' => $request->input('context'),
]
]);
$statusCode = $result->getStatusCode();
$data = json_decode($result->getBody(), true);
if ($statusCode == 200) {
$request->session()->put('store_hash', $data['context']);
$request->session()->put('access_token', $data['access_token']);
$request->session()->put('user_id', $data['user']['id']);
$request->session()->put('user_email', $data['user']['email']);
// $configValue = Config::select('*')->where('storehash', $data['context'])->get()->toArray();
// if (count($configValue) != 0) {
// $id = $configValue[0]['id'];
// $configObj = Config::find($id);
// $configObj->access_token = $data['access_token'];
// $configObj->save();
// } else {
// $configObj = new Config;
// $configObj->email = $data['user']['email'];
// $configObj->storehash = $data['context'];
// $configObj->access_token = $data['access_token'];
// $configObj->save();
// }
// If the merchant installed the app via an external link, redirect back to the
// BC installation success page for this app
if ($request->has('external_install')) {
return redirect('https://login.bigcommerce.com/app/' . $this->getAppClientId() . '/install/succeeded');
}
}
return redirect(\config('app.appUrl'));
} catch (RequestException $e) {
$statusCode = $e->getResponse()->getStatusCode();
echo $statusCode;
$errorMessage = "An error occurred.";
if ($e->hasResponse()) {
if ($statusCode != 500) {
echo "some error other than 500";
// $errorMessage = Psr7\str($e->getResponse());
}
}
// If the merchant installed the app via an external link, redirect back to the
// BC installation failure page for this app
if ($request->has('external_install')) {
return redirect('https://login.bigcommerce.com/app/' . $this->getAppClientId() . '/install/failed');
} else {
echo "fail";
// return redirect()->action('MainController#error')->with('error_message', $errorMessage);
}
}
// return view('index');
}
public function verifySignedRequest($signedRequest)
{
list($encodedData, $encodedSignature) = explode('.', $signedRequest, 2);
// decode the data
$signature = base64_decode($encodedSignature);
$jsonStr = base64_decode($encodedData);
echo $jsonStr;
$data = json_decode($jsonStr, true);
// confirm the signature
$expectedSignature = hash_hmac('sha256', $jsonStr, $this->client_secret, $raw = false);
if (!hash_equals($expectedSignature, $signature)) {
error_log('Bad signed request from BigCommerce!');
return null;
}
return $data;
}
public function makeBigCommerceAPIRequest(Request $request, $endpoint)
{
echo ' ...... trying to make an apiRequest now : with storehash : ' . $this->storehash . ' .............';
echo '...........................................';
echo 'other variables at the moment :::: ............... client ID :' . $this->getAppClientId() . '...................... token : ' . $this->getAccessToken($request) . '...............';
$requestConfig = [
'headers' => [
'X-Auth-Client' => $this->getAppClientId(),
'X-Auth-Token' => $this->getAccessToken($request),
'Content-Type' => 'application/json',
]
];
if ($request->method() === 'PUT') {
$requestConfig['body'] = $request->getContent();
}
$client = new Client();
$result = $client->request($request->method(), 'https://api.bigcommerce.com/' . $this->storehash . '/' . $endpoint, $requestConfig);
return $result;
}
public function proxyBigCommerceAPIRequest(Request $request, $endpoint)
{
if (strrpos($endpoint, 'v2') !== false) {
// For v2 endpoints, add a .json to the end of each endpoint, to normalize against the v3 API standards
$endpoint .= '.json';
}
echo ' asadssada ...... trying to make an apiRequest now : with storehash : ' . $this->storehash . ' .............' . $request->session()->get('store_hash') . ' ............ ';
$result = $this->makeBigCommerceAPIRequest($request, $endpoint);
return response($result->getBody(), $result->getStatusCode())->header('Content-Type', 'application/json');
}
}
Thanks for the detailed info. Though it would help to annotate the debug lines with confirmation of what they output, I am making the assumption that you have narrowed the problem down to the session storage and retrieval lines.
$request->session()->put() and get() are the correct ways to access session.
I would therefore suggest investigating Session configuration: https://laravel.com/docs/8.x/session#configuration
If using file-based sessions, confirm that there are no permissions errors, perhaps. Alternatively try and different session storage mechanism.

Load a PHP class multiple times

I've created a class which has multiple private an public functions and an construct function. It's an client to connect to the vCloud API. I want two objects loaded with different initiations of this class. They have to exist in parallel.
$vcloud1 = new vCloud(0, 'system');
$vcloud2 = new vCloud(211, 'org');
When I check the output of $vcloud1 it's loaded with info of $vcloud2. Is this correct, should this happen? Any idea how I can load a class multiple times and isolate both class loads?
This is part of my class, it holds the most important functions. Construct with user and org to login to. If info in the DB exists, then we authenticate with DB info, else we authenticate with system level credentials. So I would like to have two class loads, one with the user level login and one with system level login.
class vCloud {
private $client;
private $session_id;
private $sdk_ver = '7.0';
private $system_user = 'xxxxxxxxxxx';
private $system_password = 'xxxxxxxxxxxxxxxxx';
private $system_host = 'xxxxxxxxxxxxx';
private $org_user;
private $org_password;
private $org_host;
private $base_url;
public function __construct($customerId, $orgName) {
if ($this->vcloud_get_db_info($customerId)) {
$this->base_url = 'https://' . $this->org_host . '/api/';
$this->base_user = $this->org_user . "#" . $orgName;
$this->base_password = $this->org_password;
} else {
$this->base_url = 'https://' . $this->system_host . '/api/';
$this->base_user = $this->system_user;
$this->base_password = $this->system_password;
}
$response = \Httpful\Request::post($this->base_url . 'sessions')
->addHeaders([
'Accept' => 'application/*+xml;version=' . $this->sdk_ver
])
->authenticateWith($this->base_user, $this->base_password)
->send();
$this->client = Httpful\Request::init()
->addHeaders([
'Accept' => 'application/*+xml;version=' . $this->sdk_ver,
'x-vcloud-authorization' => $response->headers['x-vcloud-authorization']
]);
Httpful\Request::ini($this->client);
}
public function __destruct() {
$deleted = $this->vcloud_delete_session();
if (!$deleted) {
echo "vCloud API session could not be deleted. Contact administrator if you see this message.";
}
}
private function vcloud_delete_session() {
if (isset($this->client)) {
$response = $this->client::delete($this->base_url . 'session')->send();
return $response->code == 204;
} else {
return FALSE;
}
}
public function vcloud_get_db_info($customerId) {
global $db_handle;
$result = $db_handle->runQuery("SELECT * from vdc WHERE customer=" . $customerId);
if ($result) {
foreach ($result as $row) {
if ($row['org_host'] != "") {
$this->org_user = $row['org_user'];
$this->org_password = $row['org_password'];
$this->org_host = $row['org_host'];
return true;
} else {
return false;
}
}
} else {
return false;
}
}
public function vcloud_get_admin_orgs() {
$response = $this->client::get($this->base_url . 'query?type=organization&sortAsc=name&pageSize=100')->send();
return $response->body;
}
}
$vcloud1 = new vCloud('user1', 'system');
$vcloud2 = new vCloud('user2', 'org');
This is enough to make two instances which are not related.
I suppose your database is returning the same results.
How about providing a custom equals method to each object that retreives an instance of vCloud?
class vCloud {
//Other definitions
public function equals(vCloud $other){
//Return true if $other is same as this class (has same client_id etc etc)
}
}
So you just need to do as the code says:
$vcloud1 = new vCloud('user1', 'system');
$vcloud2 = new vCloud('user2', 'org');
if($vcloud1.equals($vclous2)){
echo "Entries are the same";
} else {
echo "Entries are NOT the same";
}
Also you may need to have various getter and setter methods into your class definitions. What is needed for you to do is to fill the equals method.

Joomla component router

I'm trying to follow the instructions on this docs page, but I seem to be missing something:
https://docs.joomla.org/Supporting_SEF_URLs_in_your_component
The URI that needs to be corrected is: index.php?com_component&view=legal&page=customermasteragreement
It seems like the routing function should be simple, but the page is just displaying default instead of the sub-view.
Here's my current code:
function ComponentBuildRoute(&$query)
{
$segments = array();
if (isset($query['view'])) {
$segments[] = $query['view'];
unset($query['view']);
}
if (isset($query['page'])) {
$segments[] = $query['page'];
unset($query['page']);
}
return $segments;
}
function ComponentParseRoute($segments)
{
$vars = array();
switch($segments[0])
{
case 'legal':
$vars['view'] = 'legal';
break;
case 'customermasteragreement':
$vars['page'] = 'customermasteragreement';
break;
}
return $vars;
}
Update
This code works to display the subpage, but it gives me a URI like: legal-agreements/legal?page=customermasteragreement
class ComponentRouter extends JComponentRouterBase {
public function build(&$query) {
$segments = array();
$view = null;
if (isset($query['view'])) {
$segments[] = $query['view'];
$view = $query['view'];
unset($query['view']);
}
if (isset($query['id'])) {
if ($view !== null) {
$segments[] = $query['id'];
} else {
$segments[] = $query['id'];
}
unset($query['id']);
}
return $segments;
}
public function parse(&$segments) {
$vars = array();
// View is always the first element of the array
$vars['view'] = array_shift($segments);
return $vars;
}
}
EDIT 2
If it helps, here's my model and views
models/legal.php
// import Joomla modelitem library
jimport('joomla.application.component.modelitem');
class ComponentModelLegal extends JModelItem {
public function __construct($config = array())
{
JLoader::register('ComponentHelper', JPATH_COMPONENT_ADMINISTRATOR . '/helpers/component.php');
parent::__construct($config);
}
/**
*
* #return string
*/
public function getLegal() {
$app = JFactory::getApplication();
$page = $app->input->get('page', '', 'STRING');
if ($page) {
ComponentHelper::add('type', $page); //This is an API request to an external service, returning JSON formatted data
$legal = ComponentHelper::getData('commons/legal-agreements.json', TRUE);
if (isset($legal[0]['status'])) {
JError::raiseError(400, $legal[0]['ERROR']);
return false;
} else {
if (!isset($this->legal)) {
$this->legal = $legal;
}
return $this->legal;
}
}
}
}
views/legal/view.html.php
class ComponentViewLegal extends JViewLegacy {
function display($tpl = null) {
// Assign data to the view
$this->legal = $this->get('legal');
// Check for errors.
if (count($errors = $this->get('Errors'))) {
JLog::add(implode('<br />', $errors), JLog::WARNING, 'jerror');
return false;
}
// Display the view
parent::display($tpl);
}
}
views/legal/tmpl/default.php
$page = JRequest::getVar('page');
$pages = array(
'resellermasteragreement',
'customermasteragreement',
'resellerdomainagreement',
'customerdomainagreement',
'resellerwebserviceagreement',
'customerwebserviceagreement',
'resellerdigicertagreement',
'customerdigicertagreement',
'registraragreement',
'customerhostingproductagreement',
'resellerhostingproductagreement'
);
?>
<div class="col-lg-12">
<?php
echo in_array($page, $pages) ? $this->loadTemplate('legal') : $this->loadTemplate('home');
?>
</div>
views/legal/tmpl/default_legal.php
$page = JRequest::getVar('page');
echo nl2br(htmlspecialchars($this->legal[$page]['defaultagreement'], ENT_NOQUOTES, "UTF-8"));
?>
<a type="button" class="btn btn-primary" href="<?php echo JROUTE::_("index.php?option=com_component&view=legal"); ?>">Back</a>
EDIT 3
This works because I wasn't navigating directly to the page from a menu item. There's a top level menu, then a page a links to the agreements. I have a hidden menu to each item, but that wasn't the link I followed.
$app = JFactory::getApplication()->getMenu();
$customermaster = $app->getItems( 'link', 'index.php?option=com_component&view=legal&page=customermasteragreement', true );
JRoute::_('index.php?Itemid='.$customermaster->id);
You have one view but you're not actually set the page argument correctly when you catch a view argument, but you treat page as some king of another view. Can you please try this and check if it works:
function [componentname]ParseRoute($segments)
{
$vars = array();
switch($segments[0])
{
case 'legal':
$vars['view'] = 'legal';
$vars['page'] = $segments[1];
break;
}
return $vars;
}
Also the functions ComponentBuildRoute, ComponentParseRoute must have your components name instead of Component at the beginning.
EDIT
[componentname]BuildRoute and [componentname]ParseRoute are deprecated, you should stick the the class that extends JComponentRouterBase as you have it in your updated second example. Try this one here and see if it works:
class ComponentRouter extends JComponentRouterBase {
public function build(&$query) {
$segments = array();
$view = null;
if (isset($query['view'])) {
$segments[] = $query['view'];
$view = $query['view'];
unset($query['view']);
}
if (isset($query['page'])) {
$segments[] = $query['page'];
unset($query['page']);
}
return $segments;
}
public function parse(&$segments) {
$vars = array();
// View is always the first element of the array
$vars['view'] = array_shift($segments);
if(count($segments) > 0)
$vars['page'] = array_shift($segments);
return $vars;
}
}
EDIT 2
I have a test Joomla 3 site with a simple component named Ola.
Non SEO component URL: http://j3.dev.lytrax.net/index.php?option=com_ola&page=test_page&view=ola
URL generated by JRoute before Router class added to router.php: http://j3.dev.lytrax.net/index.php/component/ola/?page=test_page&view=ola
SEO URL returned by JRoute::_('index.php?option=com_ola&page=test_page&view=ola'); after creating router.php and used the Router class above: http://j3.dev.lytrax.net/index.php/component/ola/ola/test_page
If you visit the links, you'll see that all are working and you can even see the page, view and JRoute results of the calling component Ola.
Unless I've completely misunderstood Joomla (I've been developing with it for four five years now), there is no "sub-view" concept implemented. Trying to use the idea is what's getting you into trouble I think.
I'm astounded that the idea of using a visual debugger is so unpopular.
Get yourself something like Netbeans (there are others too), set up a local web and database server, and watch the code run. Very (well, relatively very) quickly you'll start to understand how the structure hangs together.
You may put code in your view that picks up the extra params you've set and displays or hides things accordingly, but there is no "sub-view".

How to get the data from PUT HTTP Request (form-data)

This is my function in my API I need to get the data from the form-data from the HTTP request.
I need to get the put values to update my data in my class query.
function method()
{
$method = $_SERVER['REQUEST_METHOD'];
return $method;
}
function updateUser()
{
$method = method();
if($method == 'PUT'){
$put_data = file_get_contents("php://input");
parse_str($put_data, $post_vars);
return $post_vars;
}else{
$status = '400 Bad Request';
return $status;
}
}
{"------WebKitFormBoundaryYKcobRh4FtrGCYaI\r\nContent-Disposition:_form-data;_name":"\"test\"\r\n\r\ntest_value\r\n------WebKitFormBoundaryYKcobRh4FtrGCYaI--\r\n"}
This is what i get from the return $post_vars
I need to have test: test_value
Change the encoding of the request from multipart/form-data to application/x-www-form-urlencoded
Try replacing
$method = method();
if($method == 'PUT'){
//dosomething
}else{
//error
}
With
if (!($putData = fopen("php://input", "r"))){
throw new Exception("Can't get PUT data."); }
else{ //dostuff
}
for more information see:
http://php.net/manual/en/features.file-upload.put-method.php

save form field in component params joomla

I am using joomla 2.5, I am working on custom component of joomla . I have created the form in backend admin page. what i need is , i want to save the post data of form in params row of that component in database of #_extensions.
Here is my tables/component.php
<?php defined('_JEXEC') or die('Restricted access');
jimport('joomla.database.table');
class componentTablecomponent extends JTable {
function __construct(&$db)
{
parent::__construct('#__extensions', 'extension_id', $db);
}
public function bind($array, $ignore = '')
{
if (isset($array['params']) && is_array($array['params']))
{
// Convert the params field to a string.
$parameter = new JRegistry;
$parameter->loadArray($array['params']);
$array['params'] = (string)$parameter;
}
return parent::bind($array, $ignore);
}
public function load($pk = null, $reset = true)
{
if (parent::load($pk, $reset))
{
// Convert the params field to a registry.
$params = new JRegistry;
$params->loadJSON($this->params);
$this->params = $params;
return true;
}
else
{
return false;
}
} public function store() {
parent::store(null);
}
}
Instead of saving the $data in params row of that component . This code is creating new empty rows in that table(data is saving in those params field).
Here is my save() function in controllers/component.php
public function save()
{
// Check for request forgeries.
JSession::checkToken() or jexit(JText::_('JINVALID_TOKEN'));
// Check if the user is authorized to do this.
if (!JFactory::getUser()->authorise('core.admin'))
{
`JFactory::getApplication()->redirect('index.php',JText::_('JERROR_ALERTNOAUTHOR'));`
return;
}
// Initialise variables.
$app = JFactory::getApplication();
$model = $this->getModel('component');
$form = $model->getForm();
$data = JRequest::getVar('jform', array(), 'post', 'array');
print_r($data);
// Validate the posted data.
$return = $model->validate($form, $data);
// Check for validation errors.
if ($return === false)
{
// Get the validation messages.
$errors = $model->getErrors();
// Push up to three validation messages out to the user.
for ($i = 0, $n = count($errors); $i < $n && $i < 3; $i++) {
if ($errors[$i] instanceof Exception) {
$app->enqueueMessage($errors[$i]->getMessage(), 'warning');
} else {
$app->enqueueMessage($errors[$i], 'warning');
}
}
// Redirect back to the edit screen.
$this->setRedirect(JRoute::_('somelink',false));
return false;
}
// Attempt to save the configuration.
$data = $return;
$return = $model->save($data);
// Check the return value.
if ($return === false)
{
// Save failed, go back to the screen and display a notice.
$message = JText::sprintf('JERROR_SAVE_FAILED', $model->getError());
$this->setRedirect('index.php/somelink','error');
return false;
}
// Set the success message.
$message = JText::_('COM_CONFIG_SAVE_SUCCESS');
// Set the redirect based on the task.
switch ($this->getTask())
{
case 'apply':
$this->setRedirect('somelink',$message);
break;
case 'cancel':
case 'save':
default:
$this->setRedirect('index.php', $message);
break;
}
return true;
}
models/component.php
public function save($data) {
parent::save($data);
return true;
}
I thinks these codes are enough . I can add more codes if you need.
If you want to store params in the extension table, why not just use com_config for it?
See http://docs.joomla.org/J2.5:Developing_a_MVC_Component/Adding_configuration
Then you don't have to do anything besides creating the config.xml and adding a link to the config options in the toolbar.
I have come up with a solution . it works for me . By using this method you can control save () functions i.e. you can run your custom php scripts on save, apply toolbar buttons.
I copied save(),save($data), store($updateNulls = false) functions from com_config component , I have copied the layout from component view . Removed the buttons. Added toolbar buttons in .html.php file.
controllers/mycomponent.php
<?php
defined('_JEXEC') or die;
class componentControllermycomponent extends JControllerLegacy {
function __construct($config = array())
{
parent::__construct($config);
// Map the apply task to the save method.
$this->registerTask('apply', 'save');
}
function save()
{
// Check for request forgeries.
JSession::checkToken() or jexit(JText::_('JINVALID_TOKEN'));
// Set FTP credentials, if given.
JClientHelper::setCredentialsFromRequest('ftp');
// Initialise variables.
$app = JFactory::getApplication();
$model = $this->getModel('mymodel');
$form = $model->getForm();
$data = JRequest::getVar('jform', array(), 'post', 'array');
$id = JRequest::getInt('id');
$option = 'com_mycomponent';
// Check if the user is authorized to do this.
if (!JFactory::getUser()->authorise('core.admin', $option))
{
JFactory::getApplication()->redirect('index.php', JText::_('JERROR_ALERTNOAUTHOR'));
return;
}
// Validate the posted data.
$return = $model->validate($form, $data);
// Check for validation errors.
if ($return === false) {
// Get the validation messages.
$errors = $model->getErrors();
// Push up to three validation messages out to the user.
for ($i = 0, $n = count($errors); $i < $n && $i < 3; $i++) {
if ($errors[$i] instanceof Exception) {
$app->enqueueMessage($errors[$i]->getMessage(), 'warning');
} else {
$app->enqueueMessage($errors[$i], 'warning');
}
}
// Save the data in the session.
$app->setUserState('com_iflychat.config.global.data', $data);
// Redirect back to the edit screen.
$this->setRedirect(JRoute::_('index.php?option=com_mycomponent&view=component&component='.$option.'&tmpl=component', false));
return false;
}
// Attempt to save the configuration.
$data = array(
'params' => $return,
'id' => $id,
'option' => $option
);
// print_r($data);
$return = $model->save($data);
// Check the return value.
if ($return === false)
{
// Save the data in the session.
$app->setUserState('com_config.config.global.data', $data);
// Save failed, go back to the screen and display a notice.
$message = JText::sprintf('JERROR_SAVE_FAILED', $model->getError());
$this->setRedirect('index.php?option=com_mycomponent&view=component&component='.$option.'&tmpl=component', $message, 'error');
return false;
}
// Set the redirect based on the task.
switch ($this->getTask())
{
case 'apply':
$message = JText::_('COM_MYCOMPONENT_SAVE_SUCCESS');
print_r($data);
$this->setRedirect('index.php?option=com_mycomponent&view=myview&layout=edit', $message);
break;
case 'save':
default:
$this->setRedirect('index.php');
break;
}
return true;
}
function cancel()
{
$this->setRedirect('index.php');
}
}
models/mymodel.php
<?php
defined('_JEXEC') or die;
jimport('joomla.application.component.modelform');
class componentModelmymodel extends JModelForm {
protected $event_before_save = 'onConfigurationBeforeSave';
protected $event_after_save = 'onConfigurationAfterSave';
public function getForm($data = array(), $loadData = true){
// Get the form.
$form = $this->loadForm('com_mycomponent.form', 'config',
array('control' => 'jform', 'load_data' => $loadData));
if (empty($form)){
return false;
}
return $form;
}
public function save($data)
{
$dispatcher = JDispatcher::getInstance();
$table = JTable::getInstance('extension');
$isNew = true;
// Save the rules.
if (isset($data['params']) && isset($data['params']['rules']))
{
$rules = new JAccessRules($data['params']['rules']);
$asset = JTable::getInstance('asset');
if (!$asset->loadByName($data['option']))
{
$root = JTable::getInstance('asset');
$root->loadByName('root.1');
$asset->name = $data['option'];
$asset->title = $data['option'];
$asset->setLocation($root->id, 'last-child');
}
$asset->rules = (string) $rules;
if (!$asset->check() || !$asset->store())
{
$this->setError($asset->getError());
return false;
}
// We don't need this anymore
unset($data['option']);
unset($data['params']['rules']);
}
// Load the previous Data
if (!$table->load($data['id']))
{
$this->setError($table->getError());
return false;
}
unset($data['id']);
// Bind the data.
if (!$table->bind($data))
{
$this->setError($table->getError());
return false;
}
// Check the data.
if (!$table->check())
{
$this->setError($table->getError());
return false;
}
// Trigger the oonConfigurationBeforeSave event.
$result = $dispatcher->trigger($this->event_before_save, array($this->option . '.' . $this->name, $table, $isNew));
if (in_array(false, $result, true))
{
$this->setError($table->getError());
return false;
}
// Store the data.
if (!$table->store())
{
$this->setError($table->getError());
return false;
}
// Clean the component cache.
$this->cleanCache('_system');
// Trigger the onConfigurationAfterSave event.
$dispatcher->trigger($this->event_after_save, array($this->option . '.' . $this->name, $table, $isNew));
return true;
}
function getComponent()
{
$result = JComponentHelper::getComponent('com_mycomponent');
return $result;
}
}
tables/mycomponent.php
<?php
// No direct access
defined('_JEXEC') or die('Restricted access');
// import Joomla table library
jimport('joomla.database.table');
/**
* Hello Table class
*/
class componentTableMycomponent extends JTable
{
function __construct(&$db)
{
parent::__construct('#__extensions', 'extension_id', $db);
}
public function bind($array, $ignore = '')
{
if (isset($array['params']) && is_array($array['params']))
{
// Convert the params field to a string.
$parameter = new JRegistry;
$parameter->loadArray($array['params']);
$array['params'] = (string)$parameter;
}
return parent::bind($array, $ignore);
}
public function load($pk = null, $reset = true)
{
if (parent::load($pk, $reset))
{
// Convert the params field to a registry.
$params = new JRegistry;
$params->loadJSON($this->params);
$this->params = $params;
return true;
}
else
{
return false;
}
}
public function store($updateNulls = false)
{
// Transform the params field
if (is_array($this->params)) {
$registry = new JRegistry();
$registry->loadArray($this->params);
$this->params = (string)$registry;
}
$date = JFactory::getDate();
$user = JFactory::getUser();
if ($this->id) {
// Existing item
$this->modified = $date->toSql();
$this->modified_by = $user->get('id');
} else {
// New newsfeed. A feed created and created_by field can be set by the user,
// so we don't touch either of these if they are set.
if (!intval($this->created)) {
$this->created = $date->toSql();
}
if (empty($this->created_by)) {
$this->created_by = $user->get('id');
}
}
// Verify that the alias is unique
$table = JTable::getInstance('Yourinstance', 'mycomponentTable');
if ($table->load(array('alias'=>$this->alias, 'catid'=>$this->catid)) && ($table->id != $this->id || $this->id==0)) {
$this->setError(JText::_('COM_CONTACT_ERROR_UNIQUE_ALIAS'));
return false;
}
// Attempt to store the data.
return parent::store($updateNulls);
}
}
Note : your config.xml file should be in models/forms folder.

Categories