I want to make an instant redirection in my controller in Laravel.
I know I can use
public function show($page)
{
return Redirect::url('http://example.com');
}
but I want to repeat this code in many controllers adding condition for example I would like to do something like this:
public function show($page)
{
$totalPages = 100; // here calculating maximum page
if ($page < 2 || $page > $totalPages) {
return Redirect::url('http://example.com');
}
// rest of code here - should be run if condition is false
}
but I don't want to repeat code in each controller.
If I try put redirection code in other method (that could exist in base controller) it won't work because it doesn't return anything in main controller:
public function show($page)
{
$totalPages = 100; // here calculating maximum page
$this->checkPages($page, $totalPages, 'http://example.com');
// rest of code here - should be run if condition is false
}
public function checkPages($page, $totalPages, $url)
{
if ($page < 2 || $page > $totalPages) {
return Redirect::url($url);
}
}
How can I solve this issue?
After a while of digging it seems that for this purposes you should use send() method from Symfony\Component\HttpFoundation (Laravel RedirectResponse inherits from this class).
So you can modify checkPages method this way:
public function checkPages($page, $totalPages, $url)
{
if ($page < 2 or $page > $totalPages) {
Redirect::url($url)->send();
}
}
and it will make instant redirection.
You have to return the results of the method in the base controller.
return $this->checkPages($page, $totalPages, 'http://example.com');
I'd probably suggest that your checkPages method is trying to do too much. Would I expect checkPages (which maybe should be validatePageNumber() or similar) to return a redirect? No, I'd expect it to return true or false.
That gives you this as your logic:
/**
* Make sure the current page is between 1 and $lastPage
*
* #param int $page
* #param int $lastPage
* #return bool
*/
protected function validatePageNumber( $page, $lastPage ) {
return $page < 2 || $page > $lastPage;
}
and now your controller logic is trivial:
public function show( $page ) {
$totalPages = 100;
if ( ! $this->validatePageNumber( $page, $totalPages ) ) {
return Redirect::url( 'http://example.com' );
}
// rest of code here - should be run if condition is false
}
Now if we use quasi-English to read your code, you have a public method show($page) which shows a page: it sets the total number of pages, checks that $page is valid, and if not, redirects somewhere. All of this is control logic, and only control logic, which is what a controller should be doing.
Your validatePageNumber() can now be abstracted into a model, or better yet, a ServiceProvider, and it does one job: validates that a number is between 1 and n.
Related
I am new to WordPress plugin development. I currently am trying to develop an addition calculator plugin which has the API endpoint at example.com/calc/add. How would I add a new URL endpoint to my WordPress installation so the plugin can take in 'POST' requests (numbers in a comma-separated list) and return the data accordingly after adding the numbers? Thank you so much!
You can use parse_request hook for creating end point, below is the example from one of my plugin
// this example creates endpoint like http://emerico.in/api/v2
add_action('parse_request', 'endpoint', 0);
add_action('init', 'add_endpoint');
/**
* #param null
* #return null
* #description Create a independent endpoint
*/
function endpoint()
{
global $wp;
$endpoint_vars = $wp->query_vars;
// if endpoint
if ($wp->request == 'api/v2') {
// Your own function to process end pint
$this->processEndPoint($_REQUEST);
// After all redirect to home page
wp_redirect(home_url());
exit;
} elseif (isset($endpoint_vars['tracking']) && !empty($endpoint_vars['tracking'])) {
$request = [
'tracking_id' => $endpoint_vars['tracking']
];
$this->processEndPoint($request);
} elseif (isset($_GET['utm_source']) && !empty($_GET['utm_source'])){
$this->processGoogleTracking($_GET);
}
}
/**
* #param null
* #return null
* #description Create a permalink endpoint for projects tracking
*/
function add_endpoint()
{
add_rewrite_endpoint('tracking', EP_PERMALINK | EP_PAGES, true);
}
Minimal code
This creates the custom-endpoint like so http://example.com/custom-endpoint.
add_action('parse_request', 'register_endpoint', 0);
function register_endpoint() {
global $wp;
if ($wp->request == 'custom-endpoint') {
echo 'hello world'; // process anything here
exit;
}
}
codeigniter recursive model function returning blank but when printing it in the model showing properly
here is my code,
for controller
$commision_arr=$this->billing_model->root_commision($category_manager['id']);
and in the model
public function root_commision($id)
{
$sql="SELECT * FROM tbl_mst_category WHERE id = '".$id."'";
$query = $this->db->query($sql);
$row=$query->row_array();
if($row['parent']!=0)
{
$this->root_commision($row['parent']);
}
else
return $row;
}
recursion is tricky huh?
i think the problem is that you were only returning the id for the deepest element, but not returning that to the calling method -- so it would only work for the case where the parent id was called. i can't test the code below, but it should point you in the right direction. NB, it returns the row as an object, not as an array as your code does.
On a more academic note, if the table is large it may be better to pre-calculate these root ids for each of these categories. it will make the query much faster -- recursion is not fast. look at transitive closures
public function root_commision($id,$root_found = FALSE)
{
// returns FALSE if the id is not found, or the parent row.
$query = $this->db->get_where('tbl_mst_category', array('id' => $id));
if ($query->num_rows() > 0 )
{
$row = $query->first_row();
if (($row->parent ) != 0 )
{
return $this->root_commision($row_id);
}
else
{
return $row;
}
}
else
{
return FALSE;
}
}
you have to return function at the calling time then only you can get
the value of recursive function just add "return" keyword before function call.
public function root_commision($id)
{
$sql="SELECT * FROM tbl_mst_category WHERE id = '".$id."'";
$query = $this->db->query($sql);
$row=$query->row_array();
if($row['parent']!=0)
{
return $this->root_commision($row['parent']);
}
else
return $row;
}
I build a model side validation in Laravel 4 with the creating Model Event :
class User extends Eloquent {
public function isValid()
{
return Validator::make($this->toArray(), array('name' => 'required'))->passes();
}
public static function boot()
{
parent::boot();
static::creating(function($user)
{
echo "Hello";
if (!$user->isValid()) return false;
});
}
}
It works well but I have issues with PHPUnit. The two following tests are exactly the same but juste the first one pass :
class UserTest extends TestCase {
public function testSaveUserWithoutName()
{
$count = User::all()->count();
$user = new User;
$saving = $user->save();
assertFalse($saving); // pass
assertEquals($count, User::all()->count()); // pass
}
public function testSaveUserWithoutNameBis()
{
$count = User::all()->count();
$user = new User;
$saving = $user->save();
assertFalse($saving); // fail
assertEquals($count, User::all()->count()); // fail, the user is created
}
}
If I try to create a user twice in the same test, it works, but it's like if the binding event is present only in the first test of my test class. The echo "Hello"; is printed only one time, during the first test execution.
I simplify the case for my question but you can see the problem : I can't test several validation rules in different unit tests. I try almost everything since hours but I'm near to jump out the windows now ! Any idea ?
The issue is well documented in Github. See comments above that explains it further.
I've modified one of the 'solutions' in Github to automatically reset all model events during the tests. Add the following to your TestCase.php file.
app/tests/TestCase.php
public function setUp()
{
parent::setUp();
$this->resetEvents();
}
private function resetEvents()
{
// Get all models in the Model directory
$pathToModels = '/app/models'; // <- Change this to your model directory
$files = File::files($pathToModels);
// Remove the directory name and the .php from the filename
$files = str_replace($pathToModels.'/', '', $files);
$files = str_replace('.php', '', $files);
// Remove "BaseModel" as we dont want to boot that moodel
if(($key = array_search('BaseModel', $files)) !== false) {
unset($files[$key]);
}
// Reset each model event listeners.
foreach ($files as $model) {
// Flush any existing listeners.
call_user_func(array($model, 'flushEventListeners'));
// Reregister them.
call_user_func(array($model, 'boot'));
}
}
I have my models in subdirectories so I edited #TheShiftExchange code a bit
//Get all models in the Model directory
$pathToModels = '/path/to/app/models';
$files = File::allFiles($pathToModels);
foreach ($files as $file) {
$fileName = $file->getFileName();
if (!ends_with($fileName, 'Search.php') && !starts_with($fileName, 'Base')) {
$model = str_replace('.php', '', $fileName);
// Flush any existing listeners.
call_user_func(array($model, 'flushEventListeners'));
// Re-register them.
call_user_func(array($model, 'boot'));
}
}
I have set a field called Colour in Page.php and for any child I would like to grab the parent colour or loop through till it finds a parent that does have the colour field set.
I have a function below which seems to work in 2.4 but I cannot get to work in SS3 which I call inside a loop in templates as $Inherited(Colour).
Your help is appreciated
public function Inherited($objName) {
$page = $this->owner->Data();
do {
if ($obj = $page->obj($objName)) {
if ($obj instanceof ComponentSet) {
if ($obj->Count()) {
return $obj;
}
} elseif ($obj instanceof DataObject) {
if ($obj->exists()) {
return $obj;
}
} elseif ($obj->exists()) {
return $obj;
}
}
} while ($page->ParentID != 0 && $page = $page->Parent());
}
Assuming your Colour field is a database field and not a relationship to another data object, add the following method to your Page class.
public function getColour() {
// Try returning banners for this page
$colour = $this->getField('Colour');
if ( $colour ) {
return $colour;
}
// No colour for this page? Loop through the parents.
$parent = $this->Parent();
if ( $parent->ID ) {
return $parent->getColour();
}
// Still need a fallback position (handled by template)
return null;
}
If colour is a related data object you could do much the same thing but use the getComponent or getComponents method in place of getField in the code above. This should work on both Silverstripe version 2.4.x and 3.0.x.
This kind of operation, although useful, should probably be done sparingly or be heavily cached as it's recursive could conceivably happen on the majority of page loads, and change very infrequently.
i suppose you've had this function defined inside some DataObjectDecorator, as you're using $this->owner to refer to the current page.
there is no more DataObjectDecorator in SilverStripe 3 (see http://www.robertclarkson.net/2012/06/dataextension-class-replacing-dataobjectdecorator-silverstripe-3-0/) so there are two possible solutions:
a) replace DataObjectDecorator by DataExtension
b) simply move the Inherited function to your Page class, and replace $this->owner by $this
I asked this question and found out that we can't get the error message thrown by a DataTransformer (according to the only user who answered, maybe it's possible, I don't know).
Anyway, now that I know that, I am stucked with a problem of validation. Suppose my model is this one: I have threads that contains several participants (users).
<?php
class Thread
{
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToMany(targetEntity="My\UserBundle\Entity\User")
* #ORM\JoinTable(name="messaging_thread_user")
*/
private $participants;
// other fields, getters, setters, etc
}
For thread creation, I want the user to specify the participants usernames in a textarea, separated by "\n".
And I want that if one or more of the usernames specified don't exist, a message is displayed with the usernames that don't exist.
For example, "Users titi, tata and toto don't exist".
For that I created a DataTransformer that transforms the raw text in the textarea into an ArrayCollection containing instances of users. Since I can't get the error message provided by this DataTransformer (such a shame! Is it really impossible?), I don't check the existence of each usernames in the DataTransformer but in the Validator.
Here is the DataTransformer that converts \n-separated user list into an ArrayCollection (so that the DataBinding is ok):
<?php
public function reverseTransform($val)
{
if (empty($val)) {
return null;
}
$return = new ArrayCollection();
// Extract usernames in an array from the raw text
$val = str_replace("\r\n", "\n", trim($val));
$usernames = explode("\n", $val);
array_map('trim', $usernames);
foreach ($usernames as $username) {
$user = new User();
$user->setUsername($username);
if (!$return->contains($user)) {
$return->add($user);
}
}
return $return;
}
And here is my validator:
<?php
public function isValid($value, Constraint $constraint)
{
$repo = $this->em->getRepository('MyUserBundle:User');
$notValidUsernames = array();
foreach ($value as $user) {
$username = $user->getUsername();
if (!($user = $repo->findOneByUsername($username))) {
$notValidUsernames[] = $username;
}
}
if (count($notValidUsernames) == 0) {
return true;
}
// At least one username is not ok here
// Create the list of usernames separated by commas
$list = '';
$i = 1;
foreach ($notValidUsernames as $username) {
if ($i < count($notValidUsernames)) {
$list .= $username;
if ($i < count($notValidUsernames) - 1) {
$list .= ', ';
}
}
$i++;
}
$this->setMessage(
$this->translator->transChoice(
'form.error.participant_not_found',
count($notValidUsernames),
array(
'%usernames%' => $list,
'%last_username%' => end($notValidUsernames)
)
)
);
return false;
}
This current implementation looks ugly. I can see the error message well, but the users in the ArrayCollection returned by the DataTransformer are not synchronized with Doctrine.
I got two questions:
Is there any way that my validator could modify the value given in parameter? So that I can replace the simple User instances in the ArrayCollection returned by the DataTransformer into instances retrieved from the database?
Is there a simple and elegant way to do what I'm doing?
I guess the most simple way to do this is to be able to get the error message given by the DataTransformer. In the cookbook, they throw this exception: throw new TransformationFailedException(sprintf('An issue with number %s does not exist!', $val));, if I could put the list of non-existing usernames in the error message, it would be cool.
Thanks!
I am the one that answered your previous thread so maybe someone else will jump in here.
Your code can be simplified considerably. You are only dealing with user names. No need for use objects or array collections.
public function reverseTransform($val)
{
if (empty($val)) { return null; }
// Extract usernames in an array from the raw text
// $val = str_replace("\r\n", "\n", trim($val));
$usernames = explode("\n", $val);
array_map('trim', $usernames);
// No real need to check for dups here
return $usernames;
}
The validator:
public function isValid($userNames, Constraint $constraint)
{
$repo = $this->em->getRepository('SkepinUserBundle:User');
$notValidUsernames = array();
foreach ($userNames as $userName)
{
if (!($user = $repo->findOneByUsername($username)))
{
$notValidUsernames[$userName] = $userName; // Takes care of dups
}
}
if (count($notValidUsernames) == 0) {
return true;
}
// At least one username is not ok here
$invalidNames = implode(' ,',$notValidUsernames);
$this->setMessage(
$this->translator->transChoice(
'form.error.participant_not_found',
count($notValidUsernames),
array(
'%usernames%' => $invalidNames,
'%last_username%' => end($notValidUsernames)
)
)
);
return false;
}
=========================================================================
So at this point
We have used transformer to copy the data from the text area and generated an array of user names during form->bind().
We then used a validator to confirm that each user name actually exists in the database. If there are any that don't then we generate an error message and form->isValid() will fail.
So now we are back in the controller, we know we have a list of valid user names (possibly comma delimited or possibly just an array). Now we want to add these to our thread object.
One way would to create a thread manager service and add this functionality to it. So in the controller we might have:
$threadManager = $this->get('thread.manager');
$threadManager->addUsersToThread($thread,$users);
For the thread manager we would inject our entity manager. In the add users method we would get a reference to each of the users, verify that the thread does not already have a link to this user, call $thread->addUser() and then flush.
The fact that we have wrapped up this sort of functionality into a service class will make things easier to test as we can also make a command object and run this from the command line. it also gives us a nice spot to add additional thread related functionality. We might even consider injecting this manager into the user name validator and moving some of the isValid code to the manager.