Basically everything works if I hard code the URL in my Ajax_Controller, but I want it to access the URL from the CMS field I created.
Thanks in advance. (please ignore when I don't close my braces - just trying to copy / paste efficiently)
In /mysite/_config.php I created a custom config:
Object::add_extension('SiteConfig', 'CustomSiteConfig');
In /mysite/code/CustomSiteConfig.php I added a field where I'll store a URL:
class CustomSiteConfig extends DataObjectDecorator {
function extraStatics() {
return array(
'db' => array(
'COJsonPath' => 'Text'
)
);
}
public function updateCMSFields(FieldSet &$fields) {
$fields->addFieldToTab("Root.CO", new TextField("COJsonPath", "CO JSON URL"));
}
public function getCOJsonPath(){
return $SiteConfig.COJsonPath;
}
This successfully creates a tab in the main parent in the CMS called "CO" and a field named "CO JSON URL". I logged into my CMS and saved http://api.localhost/mymethod/ to that field.
Now I have created an Ajax page type to facilitate running Ajax commands without letting the web site user find where my APIs are, and because jQuery Ajax no likey XSS (cross site scripting).
In /mysite/code/Ajax.php:
class Ajax extends Page {
static $db = array(
);
static $has_one = array(
);
function getCMSFields()
{
$fields = parent::getCMSFields();
return $fields;
}
}
class Ajax_Controller extends Page_Controller {
public function getCO()
{
$buffer = self::createHttpRequest("http://api.localhost/mymethod/");
//$buffer = self::createHttpRequest($CustomSiteConfig::getCOJsonPath());
return $buffer;
}
This code works, but when I try to execute my createHttpRequest() with the line you see commented out, it fails. I know my syntax is wrong, I just can't figure out what it should be. Thanks for helping - I've done this before I just can't figure it out - its Friday.
I spotted several syntax errors in your code:
public function getCOJsonPath(){
return $SiteConfig.COJsonPath;
}
should be:
public function getCOJsonPath(){
return $this->owner->COJsonPath;
}
1) $SiteConfig is never defined at that point.
2) usually you would use $this, but in your case you are inside a DataObjectDecorator, so you have to use $this->owner
3) you can not use . to access properties of an object, in php you have to use ->
moving on to class Ajax_Controller, inside getCO there are the following errors:
1) $CustomSiteConfig is not defined, therefore can not be used
2) getCOJsonPath is not a static function, but you try to call it as static (again you have to use ->
so, the code should look something like this:
public function getCO() {
$siteConfig = SiteConfig::current_site_config();
$buffer = self::createHttpRequest($siteConfig->getCOJsonPath());
return $buffer;
}
that should work, but there is another think that could be improved.
As I understand it, you are creating an ajax page, which you then create once in the CMS and tell your website content authors never to touch the ajax page?
This is quiet ugly, and there are several nice ways to do what you want to do.
Here is how I would create an Ajax controller:
_config.php
// tell SilverStripe what URL your AjaxController should have,
// here we set it to AjaxController::$URLSegment which is 'myAjaxController'
// so the url to the controller is mysite.com/myAjaxController
Director::addRules(100, array(
AjaxController::$URLSegment => 'AjaxController',
));
AjaxController.php
<?php
class EventAssetsController extends Controller {
public static $URLSegment = 'myAjaxController';
// tell SilverStripe what URL should call what function (action)
// for example, mysite.com/myAjaxController/foo should call the function foo
public static $url_handlers = array(
'foo' => 'foo',
'bar/$ID/$OtherID' => 'bar',
'co' => 'getCO'
);
public function Link($action = null) {
// this function is just a helper, in case you evern need $this->Link()
return Controller::join_links(self::$URLSegment, $action);
}
public function AbsoluteLink($action = null) {
return Director::absoluteURL($this->Link($action));
}
public function foo(SS_HTTPRequest $request) {
// do something here
// this method is an action, the url to this action is:
// mysite.com/myAjaxController/foo
}
public function bar(SS_HTTPRequest $request) {
// do something here
// this method is an action, the url to this action is:
// mysite.com/myAjaxController/bar
// you notice that the $url_handlers has "bar/$ID/$OtherID",
// that means you cann call mysite.com/myAjaxController/bar/21/42
// and silverstripe will make 21 the ID, and 42 the OtherID
// you can access ID and OtherID like this:
// $ID = $request->param('ID'); // which is 21
// $OtherID = $request->param('OtherID'); // which is 42
}
public function getCO() {
// this method is an action, the url to this action is:
// mysite.com/myAjaxController/co
$siteConfig = SiteConfig::current_site_config();
$buffer = self::createHttpRequest($siteConfig->getCOJsonPath());
return $buffer;
}
}
Related
I'm looking for a way to have a single base class that can be extended by several child classes, only one of which would be active at a time. A very basic example:
class API_Base {
public $context;
public function __construct() {
$this->init()
}
}
class Mailchimp_API extends API_Base {
public function init() {
$this->context = 'mailchimp';
$this->enabled = false;
}
public function add_contact($email_address) {
// mailchimp API for adding contact
}
}
class Infusionsoft_API extends API_Base {
public function init() {
$this->context = 'infusionsoft';
$this->enabled = true;
}
public function add_contact($email_address) {
// infusionsoft API for adding contact
}
}
Each child initializes itself and registers as an option for the user to select. After the user has chosen which integration to use, this is saved to the database. I'd like future access to the API_Base to look something like:
$api = new API_Base();
$api->context; // should be "infusionsoft"
$api->add_contact($email_address);
So when $api->add_contact() is run, it only runs the add_contact() function for the active API integration.
Eventually I'd like to somehow use get_class_methods(); to return the capabilities of just the active API, so functions accessing the API can know what is possible (i.e. some API's support email lists while others don't, or support creating custom fields, etc.).
I've had some success with calling parent::set_context($context); from the enabled class, but I still can't figure out how to get the parent to only execute the methods in the "enabled" child class.
This is not how inheritance works. Child subclasses inherit from their parent class.
To solve your problem you can add a factory method to API_Base which will create API implementation by its type:
class API_Base {
public static function createByType($type)
{
switch ($type) {
case 'mailchimp': return new Mailchimp_API();
case 'infusionsoft': return new Infusionsoft_API();
default: throw new \InvalidArgumentException(spintf('Invalid API type "%s"', $type));
}
}
// other methods
}
and use it like this:
$api = API_Base::createByType($user->selectedApi);
$api->context; // should be "infusionsoft"
$api->add_contact($email_address);
You can consider Abstract Class Implementation . The abstract class works as the , who ever is extending the abstract class can execute the methods it have .
abstract class Something{
function __construct(){
// some stuff
}
function my_func(){
$this->myTest ;
}
abstract function my_func();
}
class Some extends Something{
function __construct(){
parent::__construct() ;
}
function my_test(){
echo "Voila" ;
}
}
I got it working in a way works perfectly for me, thanks to Ihor's advice. Here's what I ended up doing:
In the main plugin file, there's a filterable function where other devs can add new integrations if they need. The first parameter is the slug (for my autoloader) and the second is the class name.
public function get_apis() {
return apply_filters( 'custom_apis', array(
'infusionsoft-isdk' => 'MYPLUGIN_Infusionsoft_iSDK',
'infusionsoft-oauth' => 'MYPLUGIN_Infusionsoft_oAuth',
'activecampaign' => 'MYPLUGIN_ActiveCampaign'
) );
}
Each integration contains the slug and the class name. Then in my API_Base class I have this in the constructor:
class API_Base {
public $available_apis = array();
public $api;
public function __construct() {
$configured_apis = main_plugin()->get_apis();
foreach( $configured_apis as $slug => $classname ) {
if(class_exists($classname)) {
$api = new $classname();
$api->init();
if($api->active == true)
$this->api = $api;
$this->available_apis[$slug] = array( 'name' => $api->name );
if(isset($api->menu_name)) {
$this->available_apis[$slug]['menu_name'] = $api->menu_name;
} else {
$this->available_apis[$slug]['menu_name'] = $api->name;
}
}
}
}
}
And in my main file, after all the includes, I run:
self::$instance->api_base = new API_Base();
self::$instance->api = self::$instance->api_base->api;
Now I can call self::$instance->api->add_contact($email); and it will trigger whichever is the current active API.
It seems to be the best approach as this way I can spin up the API only once when the plugin loads, instead of having to create a new instance each time I want to use it.
Where should I put the function that loads a model and Display a view but that is not accessible from the URL. I mean, I need to write a function that should call from the view and this function displays a menu list.
function displayMenu () {
$menu = $this->model->getMenuResults();
$this->load->view( 'ViewResults', $menu);
}
Should I write this as a private function in the main controller?
You can use private functions in codeigniter controller
private function _displayMenu()
{
$menu = $this->model->getMenuResults();
$this->load->view( 'ViewResults', $menu);
}
Straight from documentation
Private Functions
In some cases you may want certain functions hidden from public access. To make a function private, simply add an underscore as the name prefix and it will not be served via a URL request. For example, if you were to have a function like this:
private function _utility()
{
// some code
}
Trying to access it via the URL, like this, will not work:
example.com/index.php/blog/_utility/
For more details go to http://ellislab.com/codeigniter/user-guide/general/controllers.html#private
Put it in one of your helper functions like this
function displayMenu () {
$CI = &get_instance();
//choose whichever is appropriate
//option 1
//if your model is globally loaded then simply call the model like this
//$menu = $CI->loadedmodel->getMenuResults();
// option 2 otherwise load the model here
//$CI->load->model('model_name');
//$menu = $CI->model_name->getMenuResults();
$CI->load->view( 'ViewResults', $menu);
}
I have a situation where I'm creating a controller file that echo's json output to be used client-side with ajax:
echo json_encode($response);
The main class in another file, among other things, grabs all the setting vars from the CMS.
Now, the controller file has a class that generates the request from the API, but the setting vars in the class (username, id, count, etc.) are hard coded because I can't figure out how exactly to get them from the main class in the other file. With the settings hard coded, the controller file creates and echos the json output as expected. It just needs the dynamic vars from the main class.
Please excuse my lack of knowledge and usage of OOP. I've been trying it with a structure like this, where again, just trying to get the username and other vars from the main class into another class within separate file.
** EDIT ** Rethinking this a bit based on the comment by #Dave Just as it makes better sense. So if I move the api_request function into mainClass and return the response, I can get the variables I need and the request still works. So that would lead me to ask - how can I still echo the $response from the api_request function in a separate file? That separate file with the json is what I'm using for my ajax script.
class mainClass {
public $username;
function __construct() {
...
}
public function api_settings( $username ) {
...
}
}
$main_class = new mainClass;
$main_class->api_settings();
// OR
$main_class->username;
api-call.php
class apiCall extends mainClass {
public $username;
function __construct() {
parent::__construct;
...
}
public function api_request() {
...
$return = $api_auth->request(
'GET',
$api_auth->url( '/cms-plug' ),
array(
//where I need to be able to grab the $username from the main class
'username' => 'joebob'
)
);
echo json_encode($response);
}
}
$api_class = new apiCall;
Since you asked me to point out this,
There are so many flaws in your architecture
First,
When you do it like,
class apiCall extends mainClass {
you break the Single Responsibility Principle and Liskov Substitution principle at the same time
Second,
A controller should never echo anything
MVC itself looks like
$modelLayer = new ModelLayer();
$view = new View($modelLayer);
$controller = new Controller($modelLayer);
$controller->indexAction($request);
echo $view->render();
You actually implementing something that is close to Model-View-Presenter, not MVC
Third
Since your class starts from api.. then there's no need to include that name in methods.
Fourth,
You don't have to tightly couple json_encode() with generation logic. That method should only return an array, then you'd json_encode() that array. Benefits? 1) Separation of Concerns 2) You can event convert that array to YAML or XML, not only JSON
And also, you should avoid inheritance in your case. Write singular class that deals with ApiCalls. So, it would look like as,
final class ApiCall
{
/**
* I'd use a name that makes sense
*
* #param string $username
* #return array on success, FALSE on failure
*/
public function fetchByUsername($username)
{
$return = $api_auth->request(
'GET',
$api_auth->url( '/cms-plug' ),
array('username' => $username)
);
if ($response !== false){
return $response;
} else {
return false;
}
}
}
And you would use it like,
if (isset($_GET['username'])){
$api = new ApiCall();
$result = $api->fetchByUsername($_GET['username']);
if ($result !== false){
// Respond as JSON
die(json_encode($result));
} else {
die('Wrong username');
}
}
You can access properties from the current object with this. This also works for inherited properties from parent classes.
api-call.php
class apiCall extends mainClass {
//public $username; // you don't have to decalre $username again, it gets already inherited from mainClass since its public there
function __construct() {
parent::__construct;
...
}
public function api_request() {
...
$return = $api_auth->request(
'GET',
$api_auth->url( '/cms-plug' ),
array(
//where I need to be able to grab the $username from the main class
'username' => this->username // vars of the current object and inherited vars are available with "this"
)
);
echo json_encode($response);
}
}
$api_class = new apiCall;
I have a little issue with Zend. I am trying to make some fake HTTP requests and after the module->controller->action is executed, to return random variables that are set in that action. Like in the case of variables set with view->assign(,) - I can access them later from view file (.phtml).
Here is part of my code:
/application/controller/IndexController.php
<?php
class IndexController extends Zend_Controller_Action
{
public function init(){}
public function indexAction()
{
#$this->view seems to be a NULL value from the fake request, so I can't pass variables to it
//$this->view->x = 'y';
echo 'test';
return array(
'x' => 'y'
);
}
}
/public/index2.php
<?php
//--cut--
$application->bootstrap();
$options = array(
'action' => 'index',
'controller' => 'index',
'module' => 'default'
);
if( isset($options['action'], $options['module'], $options['controller']) )
{
$request = new Zend_Controller_Request_Http ();
$request->setModuleName($options['module'])->setActionName($options['action'])->setControllerName($options['controller']);
$frontController = Zend_Controller_Front::getInstance ()->returnResponse ( true );
$response = new Zend_Controller_Response_Http ();
$frontController->getDispatcher ()->dispatch ( $request, $response );
echo '$response:<b>Zend_Controller_Response_Http</b><br>';
//This returns me the body of what I echo in the indexAction but not the variables.
var_dump($response);
}
Thank you so much!
If you want to assign variables to view when the request is dispatched, you can create a Zend_View instance in the indexAction and assign values, as shown below:
public function indexAction()
{
echo "test";
$view = new Zend_View();
$view->setBasePath(APPLICATION_PATH."/views/");
$view->x = 'y';
echo $view->render("index/index.phtml");
}
Try embedding the variable in the index view script, and var_dump of your response will contain both the echoed "test" and the index.phtml output.
If you want to return the array to the response, use json:
public function indexAction()
{
$array = array('x' => 'y');
echo json_encode($array);
}
index2.php:
//.....
var_dump($response);
var_dump(json_decode($response->getBody()));
You need to extend Zend_Controller_Action for this because standard Action didn't receive returned variables.
You will not be able to fetch the view parameters in the way you are trying (outside the view/MVC). This is because the action controller, in your case, IndexController only exists in memory for the duration of the dispatched method (IndexAction).
The parameters that are defined with the view class are only referenced when a call is made to view->render() and then just the HTML is generated.
You can however get the user defined variables (public scope) from within the controller action like so:
class IndexController extends Zend_Controller_Action
{
public function indexAction()
{
/** The view class uses PHP's magic set/get/isset methods to
NULL any access to values that are protected (or more precisely
have a underscore _ prefix for the property name)
hence $this->_view = null;
**/
/** we do however have access to this view (or create a new one if not already
defined within the action controller) with: **/
$view = $this->initView();
/** An array of the public controller parameters **/
$viewParams = $view->getVars();
}
}
Also, the code dispatching theses requests can now be simplified:
$front = Zend_Controller_Front::getInstance();
$front->setRequest(
new Zend_Controller_Request_Http()->setParams($options)
)->dispatch();
Note: I'm using version 1.11
I don't have alot of experience with OOP programming in PHP, and my search has given no result but solutions to direct methods. What I need is this:
// URL Decides which controller method to load
$page = $_GET['page'];
// I want to load the correct controller method here
$this->$page();
// A method
public function home(){}
// Another method
public function about(){}
// e.g. ?page=home would call the home() method
EDIT: I've tried several of the suggestions, but what I get is a memory overload error message. Here is my full code:
<?php
class Controller {
// Defines variables
public $load;
public $model;
public function __construct() {
// Instantiates necessary classes
$this->load = new Load();
$this->model = new Model();
if (isset($_GET['page'])) {
$page = $_GET['page'];
$fc = new FrontController; // This is what crashes apparently, tried with and without ();
}
}
}
If I understand your question correctly, you'd probably want something more like this:
class FrontController {
public function home(){ /* ... */ }
public function about(){ /* ... */ }
}
$page = $_GET['page'];
$fc = new FrontController;
if( method_exists( $fc, $page ) ) {
$fc->$page();
} else {
/* method doesn't exist, handle your error */
}
Is this what you're looking for? The page will look at the incoming $_GET['page'] variable, and check to see whether your FrontController class has a method named $_GET['page']. If so, it will be called; otherwise, you'll need to do something else about the error.
You can call dynamic properties and methods using something like this:
$this->{$page}();
Use a class.
Class URLMethods {
public function home(){ ... }
public function about(){ ... }
}
$requestedPage = $_GET['page'];
$foo = new URLMethods();
$foo->$requestedPage();
You can achieve this by using call_user_func. See also How do I dynamically invoke a class method in PHP?
I think you'd like also to append another string to the callable functions like this:
public function homeAction(){}
in order to prevent a hacker to call methods that you probably don't want to be.