Access data from setting Model cakePHP - php

If I am setting the Post model in my controller as follows;
$this->Post->id = $id;
Can I access the data from Post or do I have to perform execute;
$this->Post->read()
I'd like to perform some further validation on Post to determine the logic which is being executed.
e.g. If the Post['submitted_by'] = $this->Auth->user('role') == 'admin') "pseudo code"

Try This code :
$data = $this->Post->read(null, $id)

You have to read() to access the data.
It's the way the MVC works (more info there).
$this->Post->id = $id;
This code modifies the view instance, and that's all. You need another function to link the view to the database.

Related

Properly cache a type-hinted model in Laravel

I'm using Redis to cache different parts of my app. My goal is to not make a database query when the user is not logged in, as the app's content don't get updated regularly.
I cache the archive queries in my controller, however when I type hint a model in the controller, the model is retrieved from the database and then passed to the controller:
// My route
Route::get('page/{page:id}', [ PageController::class, 'show' ] );
// My controller
public function show ( Page $page ) {
// Here, the $page will be the actual page model.
// It's already been queried from the database.
}
What I'm trying to do is to try and resolve the page from the cache first, and then if the cache does not contain this item, query the database. If I drop the Page type-hint, I get the desired result ( only the id is passed to controller ) but then I will lose the benefit of IoC, automatic ModelNotFoundException, and more.
I've come across ideas such as binding the page model to a callback and then parsing the request(), but seems like a bad idea.
Is there any way to properly achieve this? I noticed that Laravel eloquent does not have a fetching event, which would be perfect for this purpose.
You can override the default model binding logic:
Models\Page.php
public function resolveRouteBinding($value, $field = null)
{
return \Cache::get(...) ?? $this->findOrFail($value);
}
Read more here https://laravel.com/docs/8.x/routing#customizing-the-resolution-logic
In order to check for existence of the data in Redis, you shouldn't type-hint the model into the controller's action. Do it like this:
public function show($pageId) {
if(/* check if cached */) {
// Read page from cache
} else {
Page::where('id', $pageId)->first();
}
}

CakePHP 2.4 redirect() with POST data?

I'm looking to send the user to another page via a controller method. The other page expects POST data.
Normally, the page is accessed with a postLink(). Is there a way to use this in the controller, perhaps with redirect()?
A little bit old but still no answer accepted so...
The answer is no, and yes.
No, there is no direct method since you cannot pass POSTed data using redirect() function.
You could use requestAction(), since you can pass data as posted (see requestAction() here for version cakePHP>=2.0).
In that case you pass an url and then an array having the key data with the posted data, something like
$this->requestAction($url, array('data' =>$this->data));or if you prefer$this->requestAction($url, array('data' =>$this->request->data));
The problem with requestAction() is that the result is environmentally as if you were generating the page of the requested action in the current controller, not in the target, resulting in not very satisfactory effects (at least not usually for me with components behaving not very nicely), so still, no.
...but Yes, you can do something very similar using the Session component.
This is how I usually do it. The flow would be something like this:
View A=>through postLink() to Action in A controller=>=>A controller request->data to Session variable=>=>action in B controller through redirect()=>=>set B controller request->data from Session variable=>=>process data in B controller action=> View B
So, in your A controller, let's say in the sentToNewPage() action you would have something like
//Action in A controller
public function sentToNewPage()
{
$this->Session->write('previousPageInfo', $this->request->data);
$url = array('plugin' => 'your_plugin', 'controller'=>'B',
'action'=>'processFromPreviousPage');
$this->redirect($url);
}
and in B controller:
//Action in B controller
public function beforeFilter()
{//not completelly necessary but handy. You can recover directly the data
//from session in the action
if($this->Session->check('previousPageInfo'))
{$this->data = $this->Session->read('previousPageInfo')};
parent::beforeFilter();
}
public function processFromPreviousPage()
{
//do what ever you want to do. Data will be in $this->data or
// if you like it better in $this->request->data
$this->processUserData($this->request->data);
//...
}
Best solution would be use javascript to redirect.
But if you want more cake I give you some tools
CakeAPI: requestAction - it allow to execute controller method of desire with parameters, if you pass 'return', it will return full view output for that action.
//very useful in views
$result = $this->requestAction('Controller/method',
array('return','user_id'=>$userId)
);
parameter will be accessible in controller via request param
$this->request->params['user_id']
Long and short is that it's not easy to emulate an HTML form with POST data and a redirect, you kind of need to set a bunch of hidden variables containing the data and automatically post the form to your destination via Javascript.
What I would do is take the processing functionality out of the function that requires POST variables, and make it generic so that you can call it from both of your functions.
Consider this rough example:
public function myPostDataAction() {
$name = $_POST['name'];
$age = $_POST['age'];
// do stuff
echo $name . ', ' . $age;
}
Let's say that is the action you are trying to post data to in this scenario, but you can't because you can't emulate those $_POST variables over a redirect without the scenario mentioned at the top here. You can do this:
public function myPostDataAction() {
$name = $_POST['name'];
$age = $_POST['age'];
// call common function
echo $this->getMyResults($name, $age);
}
// accessible from inside the controller only
private function getMyResults($name, $age) {
return $name . ', ' . $age;
}
Now you can also use that getMyResults() functionality by passing regular old variables into it:
public function myProblemFunction() {
$name = 'John';
$age = 15;
echo $this->getMyResults($name, $age);
}
Now, obviously you won't be outputting anything like that straight from the controller action, you'll be setting it to your views etc, but that's an example of how you can centralize functionality to be used in multiple locations.
For the disclaimer, this kind of thing is exactly what models are for, and you should definitely consider putting this kind of function into a model instead of a controller - it depends on your specific application.
With cakephp 2.x Use this:
$this->autoRender = false;
$request = new CakeRequest(Router::url(array('controller' => 'mycontroller','action' => 'my_action')));
$request->data('dataIndex','value');
$response = new CakeResponse();
$d = new Dispatcher();
$d->dispatch(
$request,
$response
);
but it will not redirect but dispatch to a different controller/action so if you went on /controller/oldaction it will stays the current url (no HTTP redirection is done).
You could still change the url with javascript
see: Change the URL in the browser without loading the new page using JavaScript

Condition in read() function CakePHP

In CakePHP function edit I use read() function as:
$this->data = $this->Article->read(null, $id);
It brings all fields of $id. Now, what I am trying to tweak to give one more condition in read() to get articles only if user logged in is related to it.
eg:
$this->Article->user_id = $user_id;
$this->Article->id = $id;
$this->Article->read();
And obviously it want work as read() brings data only w.r.t. $id (primary key).
My question:
Is there any way to tweak read function with condition more than $id ? Because it will just need to add a one line in all my controllers if it works ?
Or I have to use long code of find() function to get is the only option ?
Any best solution will be appreciable.
If you really must do thise, you could use OOP techniques to override the way the core method works.
Just copy the Model::read() method to your AppModel class and make the changes necessary.
You have to use find() if you want to make the search conditionally. Another option is to read the data only if the conditions hold:
$this->Article->id = $id;
if( $this->Article->field( 'user_id' ) == $user_id ) {
$this->Article->read();
}
(read() populates $this->data automatically.)

Is my PHP controller structured the wrong way? Code in the wrong place

I've fairly new to PHP MVC and I'm not 100% sold I'm doing this in the most logical way possible. This is a piece of code from one of my controllers. A friend said a majority of this code should be moved into the Model? Is this so?
Right now I have a DataAccess layer that does all the SQL work. For User work I have a UserDataAccess object, which will create, update and delete. It appears I'm using something along the lines of the Data Mapper pattern. My Model classes map directly to the database columns on a given table and that's it.
I'm continually confused at where each piece of code should go.
// Posted values passed all checks.
if(count($errors) == 0)
{
try
{
// Get a Connection.
$connection = ConnectionFactory::GetConnection('mysql');
try
{
$connection->beginTransaction();
// Create DataAccess object.
$uda = new UserDataAccess();
// Check if username and email are avail.
if(count($uda->CheckUsername($connection, $user->Username)) > 0)
$errors['username_taken'] = "Username is taken";
if(count($uda->CheckEmail($connection, $user->Email)) > 0)
$errors['email_taken'] = "Email is taken";
if(count($errors) == 0)
{
// Perform Create.
$userId = $uda->CreateUser($connection, $user->GetArray());
// Add User to Rookie Role.
$uda->AddUserToRole($connection, 1, $userId);
// Commit connection.
$connection->commit();
}
// Clear connection.
$connection = null;
// Redirect user.
if(count($errors) == 0)
header('location: /' . $user->Username);
}
catch(PDOException $e)
{
$connection->rollBack();
$errors['internal'] = "Opps: There was an error processing the request.";
}
}
catch(Exception $e)
{
$errors['internal'] = "Opps: There was an error processing the request.";
}
}
}
Try to move the mysql connection entirely to the model, the controllers should not know what type of database you are using and how you are connecting to it. Try to think of it in this way:
Views only show pages to the users;
Controllers answer to requests by performing calculations, interacting with the Models and loading Views;
You should always be able to change any of the components without affecting the ones that interact with it;
From the looks of it, it seems like some code that sound go into the controller.
Here's a break down of MVC:
Model: Accessing database, preferably using an object oriented method that treats data like object.
View: The HTML of the page.
Controller: Logic that allows a dynamic page to be built.
That probably sounded really abstract. The general idea of views is that they are the HTML template, with minimum codes, preferably only echoing certain dynamic element of the page (NOT THE HTML, just plain text, usually) and/or a bit of if and foreach loops. Example:
.... Your HTML code
<?php foreach ($page->users as $user): /* Loops through all the users */ ?>
<li><?php echo $user->name; /* echo their name */ ?></li> /
<?php endforeach; ?>
.... Your HTML Code
The idea behind controllers is how you get your page to actually work, by handling the logic, getting input, and such. Example:
class Controller extends BaseController{
function indexpage($args=array()){
if ($args[0] == 'user') $user = UserModel::listUsers(); // If the argument to the controller is 'user' (could be provided by GET, or just the URL, we won't go into that), list all users.
$this->render('yourViewPage', array('user' => $user)); // renders the view file named yourViewPage.php and pass the value of $user into a variable named 'user'. As shown by above accessed via $page->user.
}
}
Granted that the above is only a simple example, you get the point. The render() renders the page and passes the key => value pair in the array so that the view has access to them.
The Model is the database interaction. It allows you to access the database without using SQL (preferably). Example:
class UserModel{
public $name;
public $openid;
public static function listUsers($offset=0, $max=20){
global $persister;
return $persister->list('UserModel', 0, 20, array('order'=>'NAME DESC'));
}
}
// Create a new user. This usually goes into the controller
$user = User();
$user->name = 'Your User'; // sets name
$user->openid = 'htto://theiropenidprovider.com/openid'; // sets openid
$persister->track($user); // Adds the user to be tracked. (the tracking info is usually written in XML, but we won't go into that).
$persister->flushAll(); // Saves to the database (kinda like commit)
// Gets the user.
$persister->find('UserModel', 'name', 'Your User') // Find the user that has the name of "Your User" in all UserModel instanced tracked.
So Models don't have to have the most coding. In my opinions controllers would have a lot of code, but that totally depends on the complexity of what you're building.
Hope that clears it up for you.
As you said, the Model is where you should put your dabatase code above. A controller handles what the user sees. In PHP the controller is often not a class but to keep things practical, just the php file which the user navigates to, which controls what happens for that view. The controller should not need to know how the DB is implemented, or even that a DB is used, so for example having transaction information is not good.

pass a post value from view to controller to model, and back to controller in code igniter

I'm creating a login form with Codeigniter, and I have a controller that collects the inputs from the form, then I want to check to make sure what the user entered is in the database, so I'm collecting those values in the post and want to send them to the model for the database connection. Then if the results are in the database I want to send something back to the controller with a yes or no and then I can go from there. I'm kind of stuck, but this is what I have so far:
The controller:
function do_login()
{
$login = $this->input->post('Login');
$pwd = md5($this->input->post('Passwd'));
}
The Model:
function check_login()
{
$sql = $this->db->query("SELECT * FROM members WHERE loin = '?' AND password = '?'", array(//POST stuff goes in here));
return $sql->result();
}
I'm not sure how to pass the data to the model, and then back to the controller.
Any help would be great!
Thanks!
In any MVC form POST is sending to controller (in action property in form) and controller (as the name is decribed) controls what will happend, in your case should ask database for verification via model, get response, decide what to do, and use view to display results...
so in your controller:
function do_login() {
$login = $this->input->post('Login');
$pwd = md5($this->input->post('Passwd'));
$results = $this->...your_model_name...->chek_login( parameters as login and password would help )
// base on results: has records or has not - do something
// maybe display view
}
+1 to #bensiu (I tried to up the answer but wasn't able to yet)
Also: don't forget that you need to explicitly load your model.
Reading this part of the user guide should answer most of your questions.
I would add 2 parameters to check_login and make it boolean:
function check_login($user, $password)
{
$sql = $this->db->query("SELECT * FROM members WHERE loin = '?' AND password = '?'", array($user, $password));
if (...)
return TRUE;
else
return FALSE;
}
Make it boolean lets the full login checking inside the controller, so that if there are more tests to do, the developper who uses the model doesn't have to know them, so it avoids errors.
For instance if you want to check that there is only one row in the database, and an internal variable to know whether login is allowed or not (just an example).
Would all developer read the documentation about that variable that has to be checked before validating the login? Would that documentation be written?

Categories