I´ve a bit of a "strategic" problem with creating a nice mod_rewrite solution for a shop. The shop has different kind of pages:
Products (products.php)
Categories (category.php)
Content (content.php)
So the "default" URI´s look like this:
/products.php?id=2
/category.php?id=4
/content.php?id=5
The final result should be a URI´s that include the name of the page (product name, category name).
/chocholate-icecream -> product.php?id=2
/coffe-and-ice -> category.php?id=4
/imprint -> content.php?id=5
My problem now is, that I´ve different types of content which maps to different .php files. And I´m unable to determine to which .php file to route with mod_rewrite as long as I don´t use a cheat "like **/product/**chocolate-icrecream" so that I know that this URI is made for a product. But me and my customer don´t want that solution.
I´ve already started another try:
ALL requests which don´t match a physical file or folder are routed to a mapper.php file. This file looks in the database - I´ve an index with all products, categories etc. - and gives me the correct file.
The mapper then loads the content via CURL and shows it to the user
Basically I thought this was a nice idea but there are so many problems with session handling, , post data, php based redirects (header: ...) with CURL that I really plan to cancel this way etc. etc.
So my question is:
Has anyone of you an idea or a pattern that helps me creating the "good looking" URI´s without using a complex CURL mapper?
Thanks a lot in advance,
Mike
UPDATE to "Include solution":
A good example for the "include problem":
A request for www.shop.com/cart is "rewritten" to "mapper.php". The "mapper.php" decides to include www.shop.com/shoppingcart.php.
shoppingcart.php uses smarty to display the cart. The view asks the $_SERVER variable, if the actual called file is "shoppingcart.php" to decide if the page should be shown in fullscreen mode or with additional columns.
In the "normal / direct" call $_SERVER['PHP_SELF'] would be "/shopping_cart.php" which will show the fullscreen version. Going over the mapper.php with the include option would give us $_SERVER['PHP_SELF']=='/mapper.php' which will give us the view with columns (what´s wrong).
You could do this simply by pointing them all to one page and then including the proper script based on the slug. For example, rewrite all requests like index.php?slug=chocolate-icrecream.
index.php
$resource = fetch_resource($slug);
if ($resource->slug == 'product')
{
include('products.php');
}
else if ($resource->type == 'category')
{
include('category.php');
}
else
{
include('content.php');
}
I don't understand why you don't want to structure your URLs like /product/chocolate-icrecream. That would be good semantic naming that is fairly consistent across the internet nowadays.
Related
I have an application in TYPO3 CMS. It has an extension test_extension that has a controller and an action. This action should return some JSON.
class TestRequestController extends ActionController
{
public function testAction(): void
{
echo json_encode([
'test' => 123
]);
}
}
I want to be able to request this action via Postman. How can I do that? TYPO3 version - 8.7. Thanks in advance!
Creating Links
Usually extbase-extensions are created with the help of the extension extension_builder. This extension creates by default templates and links to open list- and detail-view.
It's possible to add additional actions and to create according links.
Also the usage of the templates is not required and your way to return the result of the action without usage of a template is possible.
The logic of the links is this, I break the parts down in single lines:
tx_extension_plugin[controller]=TestRequest
tx_extension_plugin[action]=test
There are still more parameters commonly used like id or search, but it's also possible to define individual parameters.
In your case the listed parameters seem to be sufficient and the link would look like this now:
www.example.com/?id=123&tx_extension_plugin[controller]=TestRequest&tx_extension_plugin[action]=test
for the extension news this would look like this, this is with your parameter-values which are not available in news. This example shows only how the extension-related part is handled (tx_news_pi1):
www.example.com/?id=123&tx_news_pi1[controller]=TestRequest&tx_news_pi1[action]=test
id is for the page here and not a parameter for your extension, else id had to look like this tx_extension_plugin[id]=123. So all extension related parameters have the form tx_extension_plugin[parameter]=value.
While it's possible to create those links with the API, it's easier to create them with the view helpers for the fluid templates. Note that sometimes an hash is added at the end, like this example: &cHash=1234567890.
The cHash-value you can't create without viewHelper or API, so the knowledge about the parameters and the other values is not enough to create the links.
Calling the link
Most often links are directly called by the browser and visible in the URL-bar. But sometimes and in your case you might call the links by AJAX, so that the json is loaded without being directly shown to the user.
It's also possible to wrap the json in script-tags, so that it's every time loaded when the whole page is called, it's not dynamic then and without AJAX it can't adjust to some user-interaction without loading a whole page again.
AJAX responses can be realized in many ways in TYPO3, the most easy one is to define a special page-type and a special page in the pagetree for it. On this page you add the plugin of your extension to return the json. This "Ajax-page" has to be configured to have the correct header for Json and must not return anything else but the JSON, so all HTML-Output has to be disabled.
Well, I am developing a plugin a and I need to display some stuff from my plugin when TYPO3 page load.
Is there some function how to hook action, like in WordPress when page loads than plugin will execute some controller method? Then this controller will produce some output a HTML, which I would like to dispaly in frontend page. Specially I would like display custom script in the head. So the script should be like this <head>...<script>my content</script>...</head>
Ok, what you probably want to do is to develop a so-called TYPO3 extension - that's what plugins/add-ons are called in TYPO3 (which is the term you will likely find google results for).
To get started fast you can try the TYPO3 extension builder (https://docs.typo3.org/typo3cms/extensions/extension_builder/) - which can generate a skeleton extension for you.
For more information you can also have a look at https://docs.typo3.org/typo3cms/CoreApiReference/latest/ExtensionArchitecture/Index.html which explains the concepts in far more detail.
Additional information is available in https://docs.typo3.org/typo3cms/ExtbaseFluidBook/Index.html
in TYPO3 there is something named plugins, but you should differ to the meaning in other context.
first TYPO3 is a CMS which content is structured in a hierarchical tree of pages. These pages are the basis for navigation. and each page contains individual contentelmenents (CE).
As Susi already told: add ons to TYPO3 are in general 'extensions' which could extend(!) the functinality of TYPO3 in different ways. one way is the definition of (TYPO3-)Plugins. These are special ContentElements which enable to show special information.
While normal CEs have all the information what to show in the record (e.g. Text & Image), plugins can be more flexible.
typical examples are: show a list of records OR one record in detail.
These Plugins can be controlled with typoscript or the plugin-CE could have additional fields to hold information what to display.
For detailed information how a plugin is defined consult the links given by Susi.
And be aware: for security reasons it is not possible to just execute a plain PHP file to echo any output. You need to register your plugin using the API, build your output as string and return the generated HTML as string to the calling function. For beginners the ExtensionBuilder will help you to generate a well formed extension which uses the API to register and output your data.
OK guys, thanks for your answers, but it was not very concrete. I found this solution, is not the best one, but it works! If anybody has better please share.
At first, you have to make a file for the class which will be called from the hook at location /your-plugin-name/Classes/class.tx_contenthook.php. Filename have to have this pattern class.tx_yourname.php Inside we will have a code with one method which will be called by the hook.
class tx_contenthook {
function displayContent(&$params, &$that){
//content of page from param
$content = $params['pObj']->content;
//your content
$inject = '4747474747';
// inject content on
$content = str_replace('</body>', $inject. '</body>', $content);
// save
$params['pObj']->content = $content;
}
}
And next, we have to call it on the hook. So Let's go to /your-plugin-name/ext_localconf.php and add there these two lines, which makes a magic and handles also caching.
// hook is called after caching
$TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['contentPostProc-output'][] = 'EXT:' . $_EXTKEY . '/Classes/class.tx_contenthook.php:&tx_contenthook->displayContent';
// hook is called before caching
$TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['contentPostProc-all'][] = 'EXT:'. $_EXTKEY .'/Classes/class.tx_contenthook.php:&tx_contenthook->displayContent';
I hope this will help those who struggling with typo3.
I am using Yii bbii forum module and it works fine. But now I want to add comments-module so every forum post could be seperately commented.
At the begining it might look:
I followed instruction what is here, but I can't make it work :(
And why I even need to include this file, if I want to add just comment?
When I added the same widget to user page (just for testing) - I got "This item cann't be commentable" and it's fine because probably I don't have correct configuration in main.php.
Difference between widget in user model view and forum view is data passed in it.
Here:
public function actionPostComment()
{
if(isset($_POST['Comment']) && Yii::app()->request->isAjaxRequest)
{
$comment = new Comment();
$comment->attributes = $_POST['Comment'];
var_dump($comment);
var_dump returned this when tried to submit comment in forum, and here in user view page.
And probably it is not even possible to combine these to modules? I'm really new in Yii.
Updated:
Basically what I have done is:
exstracted comment module (under protected->modules)
in main.php (under protected->config) added all cofiguration in modules array:
'comments'=>array(
//you may override default config for all connecting models
'defaultModelConfig' => array(
//only registered users can post comments
'registeredOnly' => false,
'useCaptcha' => false,
.......
and in view file _post.php added following:
<?php $this->widget('comments.widgets.ECommentsListWidget', array(
'model' => $data,
));
and var_dump($data) gives this (when this is called in controller where post is reseaved).
An error message was given here:
include(BbiiPost.php): failed to open stream: No such file or directory
You said that the Bbii was working with Yii and it broke when you tried to add comments.
The links to your var_dump files are broken, but I did try to read them ;)
It looks like the comments module is interfering with the POST path so that when the form submission comes in it is in a different path from the root which is confusing the YiiBase's autoloader.
You could try explicitly adding the path to BbiiPost.php to the autoloader's search path, or finding where the include("BbiiPost.php") line is and changing it to an absolute path.
Another possibility is that the forum page you are on has links to add comments but the page routing has not been taken from the route. So it might be that the POST link to the comments is actually at /forum/123/comment/add instead of just /comment/add. So when the form is submitted it is trying to the comments/add controller/action but finding that it is in /forum/view and getting confused about the paths to the include files.
I have generally found that the instructions on the Yii (v1) [v2 docs are much better] site for these modules is flaky at best. Quite often the source download link on the page points to an old buggy version of the code as the project has usually moved somewhere else. You generally need to have a pretty good PHP/Yii knowledge to debug these user-submitted modules and get them working.
Edit: I thought about a possible solution, but I made another question as it is very specific: see AJAX proxy with PHP, is it possible?
A couple of times I've encountered this problem...
I create sites that have a certain degree of modularity. So, it is possible that there are "components" (think of a rough CMS) which carry their own PHP code, CSS, and JavaScript, all dynamically included. Think about a structure like:
{siteroot}/component/datagrid/datagrid.php
{siteroot}/component/datagrid/js/datagrid.js
{siteroot}/component/datagrid/css/datagrid.css
{siteroot}/component/datagrid/ajax/getsomedata.php
Now, the question is: for JavaScript files, and expecially AJAX calls, how do I make them context-aware with the URLs?
For example, if in datagrid.js I want to call siteroot/component/datagrid/ajax/getsomedata.php with AJAX I should write (with JQuery):
$("#ajax").load("siteroot/component/datagrid/ajax/getsomedata.php");
First problem: siteroot changes on different installations. I've managed that by including a general
var codeBase = <? echo json_encode(Config::$siteRoot); ?>
with PHP on every page, from a Config file that can be easily edited for every installation, so I can do with whatever JavaScript something like:
$("#ajax").load(codeBase + "/component/Datagrid/ajax/getsomedata.php");
What do you think of this approach?
Second problem: but I have PHP functions that return to me also the components folder, or the folder of other components. It would be nice to make the whole URL dynamic. This would account also for changes in the structure of the component if I want.
The only solution I've found is to use a .js.php dynamic Javascript. This is very unelegant, and I have to include all the framework in the JavaScript file, like:
<?php
include "../../libs/framework.php"; // get my functions...
$myUrl = Config::$siteRoot . Framework::getComponentAjaxDir("datagrid") . "/getsomedata.php";
?>
$("#ajax").load(<?=json_encode($myUrl)?>);
Another side effect is that I have to know exactly the include the path for framework.php... I don't want this so hard-codedin my ".js.php" file.
Any smart solutions about that?
As nobody answered in a suitable way, I answer to myself to provide a solution I've found out that can be useful.
The key to my solution is simple:
I create an AJAX proxy at a fixed location in my site structure, so I can use codeBase to reference the proxy from JavaScript
I call this proxy with two parameters: plugin and action, which identify a) the plugin folder in which the "real" ajax is and b) the ajax file to use, along with the other params:
$("#...").load( codeBase + "/main/ajax.php?plugin=Datagrid&action=gettable&otherparams"...)
In ajax.php I sanitize the parameters, and use plugin and action to obtain the "real" ajax file:
{serverRoot}/components/{plugin}/ajax/{action}.php
Then i simply include that file in ajax.php
To be honest your problems are realistic options and aren't that bad practice in general quite frankly.
But let's explore this a little further.
What would be the best approach is for you to have 1 main config.php file which you can then specify modules, i.e. your datagrid etc.
You could store all modules in an array variable like so:
$_SITE_PATH = "/var/www/html/";
$_HTTP_PATH = "http://example.com/";
$_MODULES_PATH = $_SITE_PATH."modules/"
$_MODULES = array(
"datagrid"=>$_MODULES_PATH."datagrid/init.php",
"something_else"=>$_MODULES_PATH."something_else/init.php"
);
Each module would have it's own directory with instantiation init.php so that it would load all it required to get going.
This way you could code as you liked and when you needed something (preferably in the header state) do something like this.
global $_MODULES;
require_once($_MODULES["datagrid"]);
Everything will be easily available as and when required without any variable path issues later down the line.
I'm new to MVC and I'm trying to understand how MVC fits in to what I'm used to.
Let's say I have a simple static website with the pages: Home, About, Contact.
And let's say that I have one xhtml/css "template" which will be the view for the whole site. To keep it simple, in that tag I'll have a variable for the page contents.
So in the controller am I supposed to have an individual function for the content for each page????
For example:.
function home()
{
$data['content'] = "<p>some html home page content</p>";
$this->load->view('myView', $data);
}
function about()
{
$data['content'] = "<p>some html about page content</p>";
$this->load->view('myView', $data);
}
So while I know this is a super-simplified look, is the best practice to create a function for each different page?
But what if you have a site with 100 pages? Doesn't the controller get too big to manage?
But then again, if you had a single controller for each page, that also seems that it would be tough to manage?
I am beginning to understand how the MVC concept is helpful for database connection tasks as well as some CRUD as well. But I remain confused as how to think about "pages" in MVC.
Any advice is much appreciated.
What's the best practice?
You don't have to have a function for each page.
a quick example I've found is:
<?php
//This is an example of a KISSMVC controller
//It is simply a function, which will be called by an URL such as:
//http://example.com/article/show/234
//TIP: Please assign default values to all parameters
function _show($articleid=0) {
//SECTION 1: INPUTS
//Filter, sanitize and store all inputs used by this controller
$articleid=min(0,(int)$articleid); //zero or positive integer
$uid = isset($_SESSION['authuid']) ? $_SESSION['authuid'] : 0;
$someconfig = $GLOBALS['registry']['someconfig'];
//SECTION 2: LOGIC
//Call functions/objects that implement your business logic with the inputs from SECTION 1
//Data returned from your Model should not contain UI specifics (such as html code)
$loggedinuser = new User();
$loggedinuser->retrieve($uid);
$article = new Article();
$article->retrieve($articleid);
//SECTION 3: PRESENTATION
//Call the view templates with the data obtained from SECTION 2
//A change in UI should only affect code in this section
//Sometimes there is no output needed, only a header redirect is returned
if (!$loggedinuser->hasPermission('view_article')) {
$vars['body']='<p class="error">You have no permission to access this page!</p>';
View::do_dump(APP_PATH.'views/mainlayout.php',$vars);
exit;
}
$vars['article']=$article;
//article template is defined in views/layout/view_article.php
$vars['body']=View::do_fetch('layouts/view_article.php',$vars);
View::do_dump(APP_PATH.'views/mainlayout.php',$vars);
}
Taken from http://kissmvc.com/php_mvc_framework/code
Which is a PHP implementation of MVC (its seemed to me like you're working with PHP).
You might want to have a look at their implementation etc. It was just the first one I found. Hope this helps.
The controller in MVC tends to be the biggest part of your code base. So as far as worrying about it getting too big, that's to be expected.
However, when using MVC, you typically want the Model-View-Controller to be a 1 to 1 relationship. If you have an about page, you'll want a controller specifically for that view and the actions associated with what you can do on that page. The response may be to load another view, in which case another controller will respond to events to that. The model can be shared amongst the controller's, but you don't want any parts of your view to know anything about your model or it'll break MVC, the controller being the piece that functions as the glue.
If you are indeed doing something with PHP as dtroy mentioned, that'll probably help out. Unfortunately, I'm coming from experience with MVC on the iPhone. Same concept though all around.
In summary though:
If you keep the controller responsible solely for the actions that can occur on a given view, like button clicks, etc... it should help keep your controller from getting too large and unwieldy.
Edit:
I forgot to mention, in response to your concern which I didn't address. To help with, for instance, "Controller Explosion", you may not necessarily want to create a controller for each page, but one for each type of view. For example, you may have a AboutViewController and a ContentViewController, and maybe a loginViewController. The AboutView and LoginView would be pretty unique, but if you can reuse your contentView. So if someone requested something about "Widgets", your content view would simply display information about widgets that it retrieved from your model, and display it in the appropriate location defined by your view. then when the user requests something about "gears", you can reuse the same ContentViewController to display different different information in the same fashion.
Basically you'll have a Page controller for your Page model. Then each page that you create will have a url like "/Pages/1", which will get routed to the "show" action of your Page controller.
MVC certainly takes a bit of getting used to, but once you get the hang of it, you'll really like it. Also check out resources on REST design, which will really help with problems like this.
When I design websites now, I think in terms of resources (models, more or less). So for instance, you will have many pages or users or questions or recipes or... each of these things is a thing/model. I usually make a separate controller for each one and put 7 actions in that controller:
index - used to show a list of all the Pages (URL like "/Pages")
show - used to show an individual Page (URL like "/Pages/1")
new - used to show a form to create a new Page
edit - used to show an edit form for a Page
create - used to actually create the new Page from the new form
update - used to actually update the Page from the edit form
destroy - used to delete a Page
This isn't for everyone (I don't want to turn this into a REST debate), but it helps me a lot, and I think it makes sense when you're dealing with things that are clearly objects like Pages.
The controllers contain the empty functions if there are no actions on the page. You don't have to pass page content via controller.
Place all the content to 'view' component of current unit.
If you have a site with 100 pages, you would want most of your content to be stored in a database.
your URLs would be altered to look something like this:
http://yoursite.com/showpage?pageid=mvc
http://yoursite.com/showpage?pageid=home
http://yoursite.com/showpage?pageid=whatever
and your controller would look something like this:
function showpage()
{
$post = yourframework.getmodel('post').getPost($_GET['pageid']);
$data['title'] = "<p>" . $post.title . "</p>";
$data['content'] = "<p>" . $post.title . "</p>";
$this->load->view('myView', $data);
}
Not syntactically correct but you get the idea. You need to have only one function which delivers all 100 of your pages. This would work if all your pages have a similar structure.
In other words, mvc works just like vanilla php... (but hopefully looks a bit cleaner)