Laravel dynamic route from MySQL - dynamic

I am trying to generate routes from one template "tuning.blade.php"
I have a DB with 250 rows I would like to dynamically create 250 routes with one route or one controller.
I want to be able to use these URLs
laravel.dev/tuning/(field from DB row 1)
laravel.dev/tuning/(field from DB row 2 and so on)
I want to put the DB requests in tuning.blade.php so that this template can display all 250 rows using 250 different URLS
I have tried to use the first example from Laravel Docs
class UserController extends BaseController {
/**
* Show the profile for the given user.
*/
public function showProfile($id)
{
$user = User::find($id);
return View::make('user.profile', array('user' => $user));
}
}
Route::get('user/{id}', 'UserController#showProfile');
I also got some interesting search results from http://forumsarchive.laravel.io/viewtopic.php?id=9010
But alsas I always end up with a notfound exception
But I am unsure what to put in my tuning template to display anything at all. my Tuning template reside in app/views/home/tuning.blade.php
Currently I have got the error "Symfony \ Component \ HttpKernel \ Exception \ NotFoundHttpException"
Can anyone put me in the right direction of where I can find a resource to help me understand?

You said you want to be able to use these URLs:
laravel.dev/tuning/(field from DB row 1)
laravel.dev/tuning/(field from DB row 2 and so on)
You can do this by declaring a route like this:
Route::any('/tuning/{field}', 'TuningController#someMethod'); you may get/post
You shouldn't run sql queries from your view and if you want to really declare some dynamic routes for each fields from your database then you may do id right from your routes.php file, for example, assume that you have a table named tunings and that table contains some fields including id, name and some others. Now, to declare routes individually for each routes dynamically using the tuning field of table tunings you may create a method in your TuningController, something like this:
class TuningController extends baseController {
// other methods...
public function registerTuningRoutes()
{
$tunings = Tuning::all(); // Assume that you have a model Tuning
// Or you may use this instead
$tunings = DB::table('tuning')->get();
// Now loop all tunings and declare routes
foreach($tunings as $tuning) {
$url = '/tuning/' . $tuning->name;
$route_name = 'tuning.' . $tuning->name;
Route::any($url, $route_name); // You may use get/post
}
}
public function TuningMethod($tuning = null)
{
// $tuning will contain the current tuning name, check
dd($tuning);
}
}
Now in your routes.php file use something like this:
Registers route for each tuning name in database
App::make('TuningController')->registerTuningRoutes();
From your terminal/command prompt check the routes by running following command:
php artisan routes
But, I think, you don't need to do this, only one route is enough as I mentioned earlier in my answer.

Related

Compare two database fields in extbase repository

I am using TYPO3 8. In my extension I have a database table "company" in which I store for each company the total number of places (number_places) and the number of occupied places (occupied_places).
Now I want to limit the search to companies which have available places left.
In MySQL it would be like this:
SELECT * FROM company WHERE number_places > occupied_places;
How can I create this query in the extbase repository?
I tried to introduce the virtual property placesLeft in my model but it did not work.
I don't want to use a raw SQL statement as mentioned below, because I already have implemented a filter which uses plenty of different constraints.
Extbase query to compare two fields in same table
You can do it like this in your repository class, please note the comments inside the code:
class CompanyRepository extends \TYPO3\CMS\Extbase\Persistence\Repository
{
public function findWithAvailablePlaces(bool $returnRawQueryResult = false)
{
// Create a QueryBuilder instance
$queryBuilder = $this->objectManager->get(\TYPO3\CMS\Core\Database\ConnectionPool::class)
->getConnectionForTable('company')->createQueryBuilder();
// Create the query
$queryBuilder
->select('*')
->from('company')
->where(
// Note: this string concatenation is needed, because TYPO3's
// QueryBuilder always escapes the value in the ExpressionBuilder's
// methods (eq(), lt(), gt(), ...) and thus render it impossible to
// compare against an identifier.
$queryBuilder->quoteIdentifier('number_places')
. \TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder::GT
. $queryBuilder->quoteIdentifier('occupied_places')
);
// Execute the query
$result = $queryBuilder->execute()->fetchAll();
// Note: this switch is not needed in fact. I just put it here, if you
// like to get the Company model objects instead of an array.
if ($returnRawQueryResult) {
$dataMapper = $this->objectManager->get(\TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper::class);
return $dataMapper->map($this->objectType, $result);
}
return $result;
}
}
Notes:
If you have lots of records to deal with, I would - for performance reasons - not use the data mapping feature and work with arrays.
If you want to use the fluid pagination widget, be sure you don't and build your own pagination. Because of the way this works (extbase-internally), you'd get a huge system load overhead when the table grows. Better add the support for limited db queries to the repository method, for example:
class CompanyRepository extends \TYPO3\CMS\Extbase\Persistence\Repository
{
public function findWithAvailablePlaces(
int $limit = 10,
int $offset = 0,
bool $returnRawQueryResult = false
) {
// ...
$queryBuilder
->setMaxResults($limit)
->setFirstResult($offset);
$result = $queryBuilder->execute()->fetchAll();
// ...
}
}
I think you cant do this using the default Extbase Query methods like equals() and so on. You may use the function $query->statement() for your specific queries like this.
You also can use the QueryBuilder since TYPO3 8 which has functions to compare fields to each other:
https://docs.typo3.org/typo3cms/CoreApiReference/latest/ApiOverview/Database/QueryBuilder/Index.html#quoteidentifier-and-quoteidentifiers
It's fine to use this QueryBuilder inside Extbase repositories. After this you can use the DataMapper to map the query results to Extbase models.
In case of using "statement()" be aware of escaping every value which may cause any kind of SQL injections.
Based on the current architecture of TYPO3, the data structure is such that comparing of two tables or, mixing results from two tables ought to be done from within the controller, by injecting the two repositories. Optionally, you can construct a Domain Service that can work on the data from the two repositories from within the action itself, in the case of a routine. The service will also have to be injected.
Note:
If you have a foreign relation defined in your table configuration, the results of that foreign relation will show in your defined table repository. So, there's that too.

Laravel Query Builder : Where pivot not in

wherePivotIn is mentionend here (under Filtering Relationships Via Intermediate Table Columns) but I can't find anything about the opposite function.
As the wherePivotIn already exists but not the wherePivotNOTIn, I edited this file : vendor/laravel/framework/src/Illuminate/Database/Eloquant/Relations/BelongsToMany.php
And added this function
public function wherePivotNotIn($column, $values, $boolean = 'and', $not = false)
{
$this->pivotWhereIns[] = func_get_args();
return $this->whereNotIn($this->table.'.'.$column, $values, $boolean, $not);
}
Now the wherePivotNotIn exist and is working. But my question is:
Is it safe to update this file?
In case of update, I guess I will lose this...
After dinging a bit, I found out that the whereIn method accept more than 2 arguments.
We juste have to use it like that to use a "wherePivotNotIn"
->wherePivotIn($column,$value,'and','NotIn')
No need to declare a new class or using scope!
Yeah don't update vendor files that's a no no. Instead , create a class that extends BelongsToMany and put your implementation in there. You'll lose your changes as soon as the file updates.
Never edit what is inside the vendor directory. Create your own method to meet your need, in case it does not exist.
In your case, you can define a Local Query Scope
It will look like:
class Task extends Model
{
public function scopeWherePivotNotIn($query)
{
/**/
}
}
$tasks = Task::wherePivotNotIn()->get();

executing a sql code for creating a table and database in zend framework

I wrote a sql script and in it I created a table ;
Now I need to know ,how I can execute this script? (with which codes?)
And I have another question : where? where I must write this codes?(which folder in zend project?)
if it is possible for you please explain with an example.thanks
Creating tables in the database
Zend Framework is not supposed to be the one creating the tables, thus, my suggestion is to run those scripts in other environment.
The fastest one is, probably, the very own SQL shell, but you can use another software such as MySQLWorkbench if you are using MySQL.
Once the tables are created, the access to the tables is made this way:
Introduction
When you are using Zend Framework, you are making use of the MVC pattern. I suggest you to read what is that: Wikipedia MVC
If you read the Wikipedia link, you probably know now that the acess to the database is going to be made by the model.
Thus, if you followed the recommended project structure that Zend provides you will have a models folder under your application folder. There, you are supposed to implement the classes that will make access to the DB.
But well... you now know where to locate those classes but you will ask me: how? It's easy if you know where to search. ZF provides an abstract class called Zend_Db_Table_Abstract that has all the methods that will make your life easier talking about interaction with your database's tables. This is the class that your classes should implement.
Example
Let's suppose you've got a page in your website in which you want to show to the user a list of products of your local store. You have a table in your database called "products" in which you have all the useful information such us name, price and availability.
You will have a controller with an action called indexAction() or listAction() this action is prepared to send the data to your view and will look like:
class Store_ProductsController extends Zend_Controller_Action {
public function indexAction(){
//TODO: Get data from the DataBase into $products variable
$this->view->products = $products;
}
}
And your view file will that that products variable and do sutff with it.
But now comes the magic, you will have a class that will access to the database as I've said, it'll be like:
class Model_Store_Products extends Zend_Db_Table_Abstract{
protected $_name = 'products';
public function getAllProducts(){
$select = $this->$select()
->from(array('P'=>$this->_name),
array('id', 'name', 'price', availability));
$productsArray = $this->fetchAll($select);
return $productsArray;
}
}
And ta-da, you have your array of products ready to be used by the controller:
class Store_ProductsController extends Zend_Controller_Action {
public function indexAction(){
$model = new Model_Store_Products();
$products = $model->getAllProducts();
$this->view->products = $products;
}
}
It can be said that, since fetchAll is public function, and our select does basically nothing but set which columns do we want (it doesn't even have a where clause), in this case, it would be easier to call the fetchAll directly from the controller with no where and it will recover the whole table (all columns):
class Store_ProductsController extends Zend_Controller_Action {
public function indexAction(){
$model = new Model_Store_Products();
$products = $model->fetchAll();
$this->view->products = $products;
}
}
Thus, our function in the model is not even needed.
This is the basic information of how to access to the database using Zend Framework. Further information of how to create the Zend_Db_Table_Select object can be found here.
I hope this helps.

typo3 extbase permissions in extensions

I have written one extension for making service order.
The issue I am facing here is,
There are FE users belong to three FE user groups namely "client", "Admin" and "Employee".
Here the client can make order and he should be able to see only his orders.
And the admin can see all orders done by different clients.
And the employee should be able to see only some clients orders.
Currently I made a order table with N:1 relation with FE user table. So every order should relate with any one client.
So in controller, I am checking the login user and using custom query in repository, I am accessing order related to loggedin client (FE user)
In file OrdersController.php
public function listAction() {
$orders = $this->ordersRepository->orderForLoginUsr();
$this->view->assign('orders', $orders);
}
In file OrdersRepository.php
public function orderForLoginUsr(){
$loggedInUserId = $GLOBALS ['TSFE']->fe_user->user['uid'];
$query = $this->createQuery();
$query->matching(
$query->equals('user', $loggedInUserId)
);
$query->setOrderings(array('crdate' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_DESCENDING));
return $query->execute();
}
But here my question is how to make admin user able to see all the orders done by all clients?
I have to write different template and action that calling function findAll() ?
$orders = $this->ordersRepository->findAll();
And how to set for usergroup Employee ?
Thanks in Advance
I think that the easiest way is to actually implement 3 actions with 3 different plugins, something like: listClientAction, listAdminAction and listEmployeeAction
In each of those action, you implement a method in your repository that fetch the right list of order with the good ordering:
orderForLoginClient(), orderForLoginEmployee(), orderForLoginAdmin()
What does the trick actually is that there will be 3 plugins on your page, one for each action. In each instance of your plugin, you set the access for the right be_group.
Don't forget to add the actions and plugin in the localconf and ext_table files.
I hope it will help!
Olivier
If your view is almost the same for client, admin, employee you should simply add a method like getOrderWithPermissionsForUser($currentUser);
In the method itself you should check for the usergroup and call different queries on your Repo.
If your view is different from usergroup to usergroup, you should use different templates with partials for the same parts.
If the data of the views is the same, just change the template for each usergroup in the action. If not use different actions.
Here is a helper method for easily changing your templatefile.
/**
* This method can change the used template file in an action method.
*
* #param string $templateName Something like "List" or "Foldername/Actionname".
* #param string $templateExtension Default is "html", but for other output types this may be changed as well.
* #param string $controllerName Optionally uses another subfolder of the Templates/ directory
* By default, the current controller name is used. Example value: "JobOffer"
* #param \TYPO3\CMS\Fluid\View\AbstractTemplateView $viewObject The view to set this template to. Default is $this->view
*/
protected function changeTemplateFile($templateName, $templateExtension = 'html', $controllerName = null, AbstractTemplateView $viewObject = null)
{
if (is_null($viewObject)) {
$viewObject = $this->view;
}
if (is_null($controllerName)) {
$controllerName = $this->getControllerContext()->getRequest()->getControllerName();
}
$templatePathAndFilename = $this->getTemplateRootpathForView($controllerName . '/' . $templateName . '.' . $templateExtension);
$viewObject->setTemplatePathAndFilename($templatePathAndFilename);
}

Lithium link routing

I am trying to create my first Lithium app and I'm getting a very odd error.
I have a line in my index view,
<td><?php echo $this->html->link($question->title, array('controller'=>'questions','action'=>'view','id'=>$question->id)); ?></td>
Which I would imagine would link to that records view, and using 'questions/view'.$question->id' it does, however, using an array url I get a fatal.
Fatal error: Uncaught exception 'lithium\net\http\RoutingException' with message 'No parameter match found for URL('controller' => 'questions', 'action' => 'view', 'id' => '1').' in /Applications/MAMP/htdocs/learning-lithium/libraries/lithium/net/http/Router.php on line 306
Which to me looks like the router is trying to match the url in the helper, and as it can't, for some reason, it's throwing an exception. Does anyone have any idea's why this is? I'm attacking Lithium from a CakePHP standpoint, so this seems odd to me.
The 'args' param is handled by the default routes and get passed in as arguments to your action method.
Try this:
<?=$this->html->link($question->title, array('Questions::view', 'args' => array($question->id))); ?>
To route it with an id param, you need to specify a route that looks for an id param via {:id}. Look in the default routes.php file for the "Database object routes" section. That has some examples which I'll copy below for completeness:
/**
* ### Database object routes
*
* The routes below are used primarily for accessing database objects, where `{:id}` corresponds to
* the primary key of the database object, and can be accessed in the controller as
* `$this->request->id`.
*
* If you're using a relational database, such as MySQL, SQLite or Postgres, where the primary key
* is an integer, uncomment the routes below to enable URLs like `/posts/edit/1138`,
* `/posts/view/1138.json`, etc.
*/
// Router::connect('/{:controller}/{:action}/{:id:\d+}.{:type}', array('id' => null));
// Router::connect('/{:controller}/{:action}/{:id:\d+}');
/**
* If you're using a document-oriented database, such as CouchDB or MongoDB, or another type of
* database which uses 24-character hexidecimal values as primary keys, uncomment the routes below.
*/
// Router::connect('/{:controller}/{:action}/{:id:[0-9a-f]{24}}.{:type}', array('id' => null));
// Router::connect('/{:controller}/{:action}/{:id:[0-9a-f]{24}}');
So you would need to uncomment one of those two sections depending on what format your ids take. They use regular expressions with the id param to make sure it doesn't match url arguments that aren't ids. Incidentally, the first route is setting the default value for id to null which doesn't exactly make sense to me because I don't think the route will ever match with a null value, but anyway, that is how you set default values for your params.
Note that if you do this, your controller action method needs to look like this:
public function view() {
$id = $this->request->id;
// or an alternative that does the same thing
// $id = $this->request->get("params::id");
// ... etc ...
}
The only way to get url pieces passed in as arguments to your controller action method is to use the 'args' param.
You're not using named parameters in your route, so just output the following in your view:
<?php echo $this->html->link($question->title, array('controller'=>'questions', 'action'=>'view', $question->id));?>
Your function signature in QuestionsController should simply be:
public function view($id) {}