This is a weird question about a php mvc pattern with ajax calls. The purpose is make a better and dynamically web apps. Let me explain you:
When I learn php, I used this pattern specifically :
model.php
<?php
class myClass {
private $attrOne;
private $attrTwo;
public function getAttrOne() {
return $this->attrOne;
}
public function setAttrOne($attrOne) {
$this->attrOne = $attrOne;
}
public function getAttrTwo() {
return $this->attrTwo;
}
public function setAttrTwo($attrTwo) {
$this->attrTwo = $attrTwo;
}
// ----------------------------------------------------
public function doSelect() {
//some code
}
public function doInsert() {
//some code
}
public function doUpdate() {
//some code
}
public function doDelete() {
//some code
}
}
controller.php
<?php
require "../models/model.php";
if(isset($_POST['action'])) {
$action = $_POST['action'];
if(is_callable($action)) {
$action();
}
}
function registerSomething(){
$model = new myClass();
$model->setAttrOne($_POST['attrOne']);
$model->setAttrTwo($_POST['attrTwo']);
$return = $model->doInsert();
echo $return;
}
function registerSomething2(){
// more app logic code and other stuff
}
view.php -> this is most a pure html file with php extension
<div id="result"></div>
<form id="register" role="form" >
<input type="text" id="attrOne" name="attrOne"/>
<input type="text" id="attrTwo" name="attrTwo"/>
</form>
<script src="script.js" type="text/javascript"></script>
And the script.JS
$('#register').submit(function() {
var action = 'registerSomething';
$.ajax({
data: $(this).serialize() + '&action='+action,
url: '../controlllers/controller.php',
type: 'POST',
success: function(response) {
$('#result').html(response);
}
})
return false;
})
So, what do you think about this pattern? is this pattern efficient?
What is the best way to do ajax calls with a proper mvc pattern in php?
Is this a best practice?
If your goal was to specifically implement something MVC-like, then you have utterly failed. This setup has absolutely nothing to do with MVC. To be honest, it seems that you are way too inexperienced for tackling something like that.
If instead this is your first attempt in applying Separation of Concerns on your code, then it's appropriate. Though, I still wouldn't put that type of code in the production.
My recommendation for you would be: stop trying to "do MVC" for now, and focus instead on improving your general understanding of web development.
You should google the following topics for PHP:
url routing
autoloading and PSR4
difference between active record and data mapper pattern
request abstraction
Related
I am trying to display records from query in a table and on button clicked to make an ajax call to download the information in an xml format. The query is executed from method inside a class and the ajax request makes call to a different method inside the same class. The first method fills two private properties inside the class and the second property(the one called through the ajax request) must read the properties and fill the data inside table and make the file downloadable. When I try to read the properties from the same class though I get nulls and the foreach returns an error.
This is my ajax request(downloaderScript.js):
;
$("#downloadBtn").click(function (event) {
event.preventDefault();
event.stopPropagation();
$.ajax({
url: 'allClients.php',
type: 'post',
data: {action: 'downloader'},
complete: function (result) {
console.log(result);
},
error: function () {
console.log('Error');
}
});
});
This is the class from which I call the first and the second methods:
class HttpHandlerClient extends HttpHandlerAbstract
{
private $clientsService;
public $storedClientsHeadings;
public $storedClientsData;
public function viewAllClients()
{
$data = $this->clientsService->getAllClients(clientEntity::class);
if(isset($data)) {
$this->storedClientsHeadings = ["Client Names:", "Delivery Address:", "Phone number:"];
$this->storedClientsData = $data;
$this->render('allClientsView', $data);
}
else
{
$this->redirect('clientAdd');
}
}
public function downloader()
{
header("Content-Type: text/plain");
var_dump($this->storedClientsHeadings);
foreach ($this->storedClientsHeadings as $arrayName)
{
echo implode("\t", $arrayName)."\r\n";
}
/**
* #var clientEntity $clientData
*/
foreach ($this->storedClientsData as $clientData)
{
echo implode("\t", $clientData->getClientName())."\r\n";
echo implode("\t", $clientData->getAddressForDelivery())."\r\n";
echo implode("\t", $clientData->getPhone())."\r\n";
}
$filename = "clients_".date("Y-m-d").".xls";
header("Content-Disposition:attachment; filename=\"$filename\"");
header("Content-Type: application/vnd.ms-excel");
}
And this is the php file that i use between my ajax request and the php class(The file name is: allClients.php, in common.php I make an instance of the class HttpHandlerClient):
require_once 'common.php';
if(isset($_POST['action'])){
$myHttpHandlerClient->downloader();
} elseif (isset($_GET['typer'])) {
$myHttpHandlerClient->viewClientByNumber($_GET['typer']);
} else {
$myHttpHandlerClient->viewAllClients();
}
Sorry if my question is trivial, I even started doubting that after require_once I re-run the code in common.php, making a new instance of HttpHandlerClient and because of this I get nulls in the properties. But when I was reading the documentation in php's site I did not read such a thing. Any help will be appreciated, thanks.
It sounds like what may be happening is that there is a disconnect between the javascript initially loading and then more markup being added to the DOM.
The page loads up which runs your javascript. At this point the JS only knows what is currently on the page.
Your make the first call which changes the DOM. Your javascript does not know about these changes.
You try to reference something that has not been recognized by your initial load of the Javascript.
After you make the call that changes the DOM, you may have to reinitialize your JS to recognize the changes.
Good luck
I enjoy using Slim, but am frustrated on how to debug it. Lets say I have the following routing. I can do a cURL request, and see the output, but instead I wish to go through the script line by line with my IDE debugger which happens to be NuShere's phpED. While I haven't quick figured it out, I am pretty sure I can make phpED do a POST request, but definitely not a PUT or DELETE request, so there is no point.
Is there any way to do so? I can force $_SERVER['REQUEST_URI'] and $_SERVER['REQUEST_METHOD'] to any value easy enough, and can also change the $_GET superglobal, but body data (i.e. $_POST) isn't so simple.
<?php
$app = new \Slim\Slim();
$app->get('/books/:id', function ($id) {
//Show book identified by $id
});
$app->post('/books', function () {
//Create book
});
$app->put('/books/:id', function ($id) {
//Update book identified by $id
});
$app->delete('/books/:id', function ($id) {
//Delete book identified by $id
});
$app->get('/toys/:id', function ($id) {
//Show toy identified by $id
});
$app->post('/toys', function () {
//Create toy
});
$app->put('/toys/:id', function ($id) {
//Update toy identified by $id
});
$app->delete('/toys/:id', function ($id) {
//Delete toy identified by $id
});
Based on Blake's comments, I created the following method, and call it in a constructor. After each request, a new test file is created which can be debug.
private function createTest($params,$method)
{
$inputs=print_r($params,1);
$method=strtolower($method);
$method2=($method=='put' || $method=='delete')?'post':$method;
$html=$this->createTestInputs($params,null,null); //array, previous inputs, name prefix
$html=<<<eod
<p>URL: $_SERVER[REQUEST_URI]</p>
<p>METHOD: $method</p>
<p>INPUTS:<pre>$inputs</pre></p>
<form action="$_SERVER[REQUEST_URI]" method="$method2">
$html
<input type="hidden" name="_METHOD" value="$method"/>
<input type="submit" value="Test"/>
</form>
eod;
file_put_contents($_SERVER['DOCUMENT_ROOT'].'/test.php', $html);
}
private function createTestInputs($params,$html,$prefix)
{
foreach($params as $name=>$value) {
if(is_array($value)) {
$html=$this->createTestInputs($value,$html,$name);
}
else {
$html.="<input name='".($prefix?$prefix.'['.$name.']':$name)."' type='hidden' value='$value' />\n";
}
}
return $html;
}
If You need debug info in case of exception:
$app->config('debug', true);
also You can use Firebug + FirePHP as explained here: https://www.sitepoint.com/debug-php-firebug-firephp/
Tracy
but I like the interface of debugging so I found something like this: https://github.com/nette/tracy
checkout this gist: https://gist.github.com/1f7/b2d2846777e71f48e43a2ef7acc0a655 (it has little bugs in example, but You can handle it)
demo is here: https://nette.github.io/tracy/tracy-debug-bar.html
screenshot of my test implementation:
but I really recommend You to use normal IDE like PHPStorm and debug Your app like a boss (:
watch: https://www.youtube.com/watch?v=rqDDJfG6ip4 or this: https://www.youtube.com/watch?v=_U8xrj0-PXU
p.s. for question of properly configuring Tracy, write in comment (;
Slim3 Tracy gist increased to Slim3 Debug Bar package.
More screenshots here.
And i agree - PHPStorm is better for debug, but quick discover vars easier in Tracy.
In the near future plan add Echo Console (JQuery Terminal)
I'm using CodeIgniter for over a year now (and got pretty used to it) then found out that it is getting outdated and support has been taken off by ellislab. I want to try using Laravel as an alternative though CodeIgniter still works just fine but for Future purposes, I don't want to get left behind. Now, with Laravel's MVC framework it was easy for me to migrate some of my projects from CI. I'm having a problem with Laravel's routing though as I'm not able to communicate with my controller. Here's a bit of my code:
controller.php:
public function connect() {
$this->load->model('home_model');
if ($DBconnect=$this->home_model->checkConnection()) {
echo json_encode(array(
"success" => true,
"db_connect" => $DBconnect
));
} else {
echo json_encode(array(
"success" => false,
"db_connect" => $DBconnect
));
}
}
view.js:
$("#connection").click(function(e) {
e.preventDefault();
show_loading();
$.get('./home/connect', function(data) {
hide_loading();
Materialize.toast(data.db_connect, 4000, 'rounded green');
}, "json");
});
PS: Am I doing the Laravel way or I'm still very CI minded?
This is my way to connect with the controller
First set up the routes.php :
Route::controller('/','MyController');
On the controller app/controller/MyController.php (You need to create one) :
<?php
class MyController extends BaseController {
public function getJustASampleText(){
echo "Hello World";
}
public function getJustASampleSmile(){
echo ":D";
}
public function getConnect(){
$var_value = "Hello world";
return Response::json(array('var_name' => $var_value));
}
}
Then you can call that function like this
domain.com/just-a-sample-text or
domain.com/just-a-sample-smileor in you case
$.get(`http://127.0.0.1/laravel/public/connect`,function(data){
alert(data.var_name);
})
That how we use Route::controller approach.
I'm trying to figure out how to use one of my view elements inside of a controller...
I know, I know: "Don't do that!" (99% of the time this is the correct answer)
But I think I actually have a good reason. The action is handling an AJAX request which returns markup. The returned markup is a list which I display everywhere else using an element. So in an effort to keep my code DRY, I think it's appropriate to do this here.
Is this possible?
Easy:
$view = new View($this, false);
$content = $view->element('my-element', $params);
Also:
DON'T DO THAT ANYMORE!!!
Sometimes, you need to render a CakePhp element from a view and inject its content into the page using AJAX the same time. In this case rendering element as a regular view from controller is better than creating a dedicated view that just contains <?php echo $this->element('some_element') ?>, and may be done this way:
<?php
public function ajax_action() {
// set data used in the element
$this->set('data', array('a'=>123, 'b'=>456, 'd'=>678));
// disable layout template
$this->layout = 'ajax';
// render!
$this->render('/Elements/some_element');
}
I know this is an old question and other people have already given basically the same answer, but I want to point out that this approach (provided by Serge S.) ...
<?php
public function ajax_action() {
// set data used in the element
$this->set('data', array('a'=>123, 'b'=>456, 'd'=>678));
// disable layout template
$this->layout = 'ajax';
// render!
$this->render('/Elements/some_element');
}
...is not a hacky workaround, but is in fact the recommended approach from the CakePHP docs for this common and legitimate use case:
If $view starts with ‘/’, it is assumed to be a view or element file
relative to the /app/View folder. This allows direct rendering of
elements, very useful in AJAX calls.
(Again: Credit to Serge S. for the code above)
$this->view = '/Elements/myelement';
You should use a client-side template. You should never return mark-up from a web service or API, just data. Have your JavaScript take the data, and then format it how you wish.
For example:
function getItems() {
$.get('/some/url', function(response) {
if (response.data.length > 0) {
for (var i = 0; i < response.data.length; i++) {
var item = response.data[i];
$('.results').append('<li>' + item.title + '</li>');
}
}
});
};
This is just an example written off the cuff. Obviously you’ll need to write your own implementation.
The way I did any ajax handling in Cake was to have my own AjaxController. Any interaction of ajax-kind goes there, which in-turn uses their own views (and view partials / elements). That way you can keep your code DRY and isolate and propagate all ajax use-cases there.
Example excerpt:
<?php
class AjaxController extends AppController {
/**
* (non-PHPdoc)
* Everything going to this controller should be accessed by Ajax. End of story.
* #see Controller::beforeFilter()
*/
public function beforeFilter() {
parent::beforeFilter();
$this->autoRender = false;
$this->layout = false;
if (!$this->request->is('ajax')) {
$this->redirect('/');
}
}
public function preview() {
if ($this->request->is('ajax')) {
$this->set('data', $this->data);
$this->render('/Elements/ajaxpreview');
}
}
?>
Here's the source: https://github.com/Sobient/dosspirit/blob/master/app/Controller/AjaxController.php
im using AMFPHP to link webservices into my app
this is what I wanna do:
gateway.call("Medic.GetSupplierByZipCode("10027")",new Responder(success,fail));
the thing is that Medic.GetSupplierByZipCode() goes parametrized, and I'm not recieving any content for my data grid.
So, how do you send a parametrized call to AMFPHP?
I found the solution.
AS (Snippet)
public function init():void
{
var params:Array = new Array("param1", "param2");
gateway.connect("http://localhost/amfphp/gateway.php");
gateway.call("TestService.testFunction", rs, params);
}
PHP
<?php
class TestService {
public function testFunction($params) {
return $params[0] . $params[1];
}
}
?>
There we go.