I would like to have the same action be performed by 2 or more route patterns.
For example:
//Route 1:
Get["/{category}/{product_name}/{id}"]
// Route 2:
Get["/api/products/{id}"]
Ideally the first route would be SEO friendly and return a view, the second route would return JSON and be used as an API.
Is it simply a matter of defining 2 separate routes and calling the common logic encapsulated in another method? Or is there some Nancy magic I don't know about?
Update
My final solution was to use multiple assignments in the one statement.
Get["/{category}/{product_name}/{id}"] = Get["/api/products/{id}"] = params =>
{
...
};
I know this is answered, but I thought I'd add my tuppence for a slightly neater solution. This is my solution:
public class ExampleModule : NancyModule
{
public ExampleModule()
{
Get["/somepath"] = DoSomething;
Post["/somepath"] = DoSomething;
}
private dynamic DoSomething(dynamic parameters)
{
return null;
}
}
It's a matter of calling the common logic. No magic I'm afraid.
Note that Nancy's content negotiation can take care of returning a view or json based on the request.
Related
I'm using Laravel for a site where most database objects can be private (i.e., viewed only by their owner) or public (viewed by everyone, including guests). Each of these has a user_id, which I set to NULL when the object is public.
What's the simplest way of authenticating routes for this scenario? For example, in /routes/web.php I have:
Route::get('/{tournament}/players', [TournamentController::class, 'indexPlayers']);
and I want to make sure that tournament->user_id is either NULL or corresponds to the user's id. I was able to do this by explicitly binding tournament in /app/Providers/RouteServiceProvider.php:
Route::bind('tournament', function ($hash) {
$user_id = Auth::user()->id ?? NULL;
return Tournament::where([['hash', $hash], ['user_id', $user_id]])
->orWhere([['hash', $hash], ['user_id', NULL]])
->firstOrFail();
});
but I have the strong feeling that I'm making it too complicated or doing things in the wrong place. Is there a better way? Should I by doing this inside TournamentController, for example?
First, there is now the syntax Auth::id() that can be used as a shorthand for Auth::user()->id ?? NULL, so that saves some trouble.
Next, I ended up moving the logic out of RouteServiceProvider.php and into the controller, so that I can explicitly control what happens for public vs. private objects:
class TournamentController extends Controller
{
public function indexPlayers(Tournament $tournament)
{
if ($tournament->user_id === NULL) {
// public
} else if ($tournament->user_id === Auth::id()) {
// private
} else {
// unauthorized
}
}
...
}
I'm new in CakepHP and I want to use a method (that returns a value) in an action in CakePHP 3. Sort of like this:
public function specify(){
if(isObject1){
// do something}
}
private isObject1($objname){
return true;
}
What is the right syntax?
CakePHP is PHP
The way to call a method from another method of the same class is the same as with any php project using objects - by using $this:
public function specify() {
$something = 'define this';
if($this->isObject1($something)) {
// do something
}
}
private function isObject1($objname) {
return true;
}
There's more info on how to use objects in The PHP manual.
The answer by #AD7six suggests adding a method within the controller which is not right if it is not going to be used as an action.
I think you should consider creating classes under vendor and including them in your controller and calling your class/method. The convention is vendor/$author/$package. You can either autoload them with composer or use a require call to include your file. If you don't want to create a class and just want to have functions, that can be done too.
Do take a look at cakephp's loading vendor files section.
I am writing a personal bookmarks application and am looking for a way to have totally different content between subdomains.
I have written the application and it works fine, but have yet to implement multiple collections. I have the following models:
Tag (unused so far)
Bookmark
Subject
Bookmarks are categorized in subjects and I'm planning to allow tagging in the future and see if that helps me manage my bookmarks more easily.
The current problem I have is that I'd like to separate bookmarks as a whole. I want to use subdomains like webdevelopment.bookmarks.local, languages.bookmarks.local, linux.bookmarks.local that work with an entire own set of domains and bookmarks.
I am considering adding a new model called Set (short for "bookmark sets") and defining sets based on the subdomain.
According to that plan I'd have to rewrite all $this->...->find-queries in the entire App to contain the condition "set_id" = $SubdomainBasedSetid".
While it wouldn't be that much work, I was wondering if it could be done smarter, maybe that Cake would only see the relevant bookmark set per subdomain.
well, your solution is right. But instead of using subdomains, you can use prefix, so you don't have check and set yourself. Since you use subdomains, I assume that these sets are fixed or rarely change. So you don't really need a sets table, just use the set name directly in your bookmark record, so you don't have to convert between name and id.
According to that plan I'd have to rewrite all $this->...->find-queries in the entire App to contain the condition "set_id" = $SubdomainBasedSetid".
As all models extend AppModel, it is possible to edit all queries there before they happen (ie. DRY). :)
// app/app_model.php
class AppModel extends Model {
public function beforeFind($queryData) { // old query
// make changes
return $queryData; // new query
}
However, if you don't want this functionality for all models (or even if you do for now), a better place might be a behavior as this allows you to pick and choose where and when it is loaded:
// app/models/behaviors/subdomain.php
class SubdomainBehavior extends ModelBehavior {
protected $_defaults = array('field' => 'Site.subdomain');
public function setup(&$model, $config = array()) {
$this->settings[$model->alias] = array_merge($this->_defaults, $config);
}
public function beforeFind(&$model, $queryData) {
$domain = $_SERVER['SERVER_NAME'];
$subdomain = substr($domain, 0, strpos($domain, "."));
$queryData['conditions'][$this->settings['field']] = $subdomain;
return $queryData;
}
}
// app/app_model.php
class AppModel extends Model {
$actsAs = array('Subdomain' => array('field' => 'Set.slug'));
}
Tag (unused so far)
To save reinventing the wheel, you may want to look at CakeDC's tags and utils plugins (the latter contains SluggableBehavior which will help with generating friendly URL parts, such as the subdomains).
Some background on what I'm doing
I usually like to have my pages return the url needed to access it. So i will normally have a method like so
public partial class ProductDetails : Page
{
public static string GetUrl(Guid productId)
{
return "fully qualified url";
}
}
on my other pages/controls that need to access this page i'll simply set the link as
hl.NavigateUrl = ProductDetails.GetUrl(id);
I'm toying around with the new UrlRouting stuff in 4.0 and ran into something I'm not sure will work. I'm trying to use the Page.GetRouteUrl in my static method, and obviously it's blowing up due to Page not being static.
Does anyone know if it's possible replicate what i'm doing with GetRouteUrl?
thx
You can do something like:
var url = ((Page)HttpContext.Current.Handler).GetRouteUrl(id);
Note: If you called this method from another page, you may not get the desired result if it's relative-specific in some way...but it's as good as you can get with static I believe.
I got GetRouteUrl to work using Nicks suggestion above.
I also found an alternative way to do it w/o using the GetRouteUrl. You are basically generating it manually using GetVirtualPath
public static string GetUrl(int productId)
{
var parameters = new RouteValueDictionary { { "productId", productId } };
var vpd = RouteTable.Routes.GetVirtualPath(null, "product-details", parameters);
return vpd.VirtualPath;
}
I'm looking for some help understanding how to generate pages from a database to create a catalog of items, each with different URLs. All I can seem to find through google are products that will do this for me, or full e-commerce solutions. I don't want a shopping cart! Just an inventory.
Also, perhaps someone could recommend their favorite/the best simple login solution.
Thank you so much for your time and any help, suggestions, comments, solutions.
I just posted a thorough solution to another question that is very closely-related to this question. I'll re-post it here for your convenience:
I would suggest using some of the MVC (Model, View, Controller) frameworks out there like KohanaPHP. It is essentially this. You're working in a strictly Object-Oriented environment. A simple page in Kohana, build entirely from a class would look like this:
class Home_Controller extends Controller
{
public function index()
{
echo "Hello World";
}
}
You would then access that page by visiting youur url, the class name, and the method name:
http://www.mysite.com/home/ (index() can be called after home/, but it's implicit)
When you start wanting to bring in database-activity, you'll start working with another Class called a Model. This will contain methods to interact with your database, like the following:
class Users_Model extends Model
{
public function count_users()
{
return $this->db->count_records('users');
}
}
Note here that I didn't write my own query. Kohana comes with an intuitive Query Builder.
That method would be called from within your Controller, the first class that we mentioned at the beginning of this solution. That would look like this:
class Home_Controller extends Controller
{
public function index()
{
$usersModel = new Users_Model;
$userCount = $usersModel->count_users();
echo "We have " . $userCount . " users!";
}
}
Eventually, you'll want more complicated layouts, which will involve HTML/CSS/Javascript. At this point, you would introduce the "Views," which are just presentation layers. Rather than calling echo or print from within the Controller, you would load up a view (an HTML page, essentially) and pass it some variables:
class Home_Controller extends Controller
{
public function index()
{
$myView = new View("index");
$usersModel = new Users_Model;
$userCount = $usersModel->count_users();
$myView->userCount = $userCount;
$myView->render(TRUE);
}
}
Which would load the following "View"
<p>We have <?php print $userCount; ?> users!</p>
That should be enough to get you started. Using the MVC-style is really clean, and very fun to work with.
There's a lot of tools out there for generating a web interface around a data model. I find Django pretty easy to use. Based on its popularity, I'm sure that Ruby on Rails is another viable option.