Symfony function render parameters dynamique - php

Hello everybody I come towards you because I have small one concerns and I hope that to go to be able to you help me, I to create a shop with symfony but I have a small problem with the function "render ()" when I makes cross id in the controller kind by putting 3 or 4 for example it works but when I puts him dynamically that does not work
i share my code
my controller
/**
* #Route("/produits-views/{id}", name="product-views", methods="GET")
* #param int $id
* #return Response
*/
public function viewsProduct(int $id): Response {
$product = $this->getDoctrine()->getRepository(Product::class)->find($id);
return $this->render('inc/views-product.html.twig', [
'productViews' => $product
]);
}
and my function render
{{ render(controller('App\\Controller\\FrontController::viewsProduct',
{id: productViews.id}
)) }}
i include this on footer.html.twig and footer is include in base.html.twig
He takes out to me a message of this type
Variable "productViews" does not exist.
thank you for you helps !

change your action to let Symfony takes care of the fetching process:
/**
* #Route("/produits-views/{id}", name="product-views", methods="GET")
* #param Product $product
* #return Response
*/
public function viewsProductAction(Product $product): Response {
return $this->render('inc/views-product.html.twig', [
'productViews' => $product
]);
}
calling render with productViews.id in base.twig requires that in every view that extends this template need to have the productViews variable accessible which means that it have been provided to the view with render parameters.
you need to think about it, does all the views you have provide this variable like a user in the session if not remove it, you can alter the footer content from the view if you need to show the product according to each view using a dedicated Block for footer.

Related

PHP/MVC : in this case, how to use DTO?

I'm new to OOP and MVC with PHP, and I'm currently learning by making my own custom framework from scratch, for testing purposes. I have set up my controllers, models and views and everything works fine.
My app has the following architecture :
It’s a small blog that follows the rules of the MVC pattern. To summarize, it works like this :
The called Controller will fetch the data using the right models
Models return objects of the class \Classes\{MyObject}
Controller call the right template to render the view, and passes it the data and objects to display
The problem
In some views, I need to display related data. For example, in the article view, I need to display the author's first name. In the database, an article contains only the author’s ID, not his first name : this is the same thing in my class \Classes\Article.
What I've tried
To display the author’s first name in my view, I've updated the model Find method to use a LEFT JOIN in the SQL query. Then, I've updated my \Classes\Article class to have a user_firstname property :
class Article
{
private $pk_id;
private $title;
private $excerpt;
private $content;
private $created_at;
private $fk_user_id;
private $updated_at;
private $user_firstname; // <-- I've added this property to retrieve author's firstname
// (...)
}
What I did works well, but my teacher tells me it’s not the right way to do it because the author’s firstname is not part of the definition of an article.
In this case, my teacher tells me to use a DTO (Data Transfert Object) between Article and User classes.
Questions
What is the right way to set up a DTO in this case?
Do I need to create a new ArticleUserDTO class in a new namespace ?
How to use it ?
I think I understood the problem : the Article class should only contain what defines an article. But I can’t understand the logic of setting up a DTO. I’ve done some research on it, I understand the usefulness of the DTO but I can’t set up into my app.
Setting up a DTO in my app was easy ! As mentioned by #daremachine, the diagram below helped me to understand what a DTO is for.
Diagram source : martinfowler.com
We can see DTOs as an object assembler, in which we place all the elements we need on the view side.
For example, in the post view of an article, I needed to display other items, such as the author and posted comments. So I have created a Post class that groups all these items.
Setting up a DTO
In my \Classes\ namespace, I've created a new Post class. First, we define the properties we will need. Then we add the getters and setters for each of them. Finally, we set up the constructor, which will call each of the classes we need in the view.
namespace Classes;
use DateTime;
class Post
{
private int $pk_id;
private string $title;
private string $excerpt;
private string $content;
private DateTime $created_at;
private DateTime $updated_at;
private int $author_id;
private string $author_firstname;
private array $comments;
public function __construct(Article $article, User $author, array $comments)
{
$this->setPkId($article->getId());
$this->setTitle($article->getTitle());
$this->setExcerpt($article->getExcerpt());
$this->setContent($article->getContent());
$this->setCreatedAt($article->getCreatedAt());
$this->setUpdatedAt($article->getUpdatedAt());
$this->setAuthorId($article->getAuthorId());
$this->setAuthorFirstname($author->getFirstname());
$this->setComments($comments);
}
/**
* #param int $pk_id
*/
public function setPkId(int $pk_id): void
{
$this->pk_id = $pk_id;
}
/**
* #return int
*/
public function getPkId(): int
{
return $this->pk_id;
}
// (etc)
}
We now need to update the ArticleController, which should no longer pass the Article, Comment and User objects, but only the new Post object.
namespace Controllers;
class ArticleController extends Controller
{
// (...)
/**
* Get an article and display it
*
* #return void
*/
public function show(): void
{
// (...)
// Find Article :
$article = $this->articleModel->find($article_id);
if (!$article) {
Http::error404();
}
// Find Comments :
$commentaires = $this->commentModel->findAllByArticle($article_id);
// Find User (author)
$user = $this->userModel->find($article->getAuthorId());
// Data Transfert Object instance :
$post = new Post($article, $user, $commentaires);
$pageTitle = $post->getTitle();
// Pass DTO to view :
Renderer::render('articles/show', compact('pageTitle', 'post'));
}
}
We just need to update our view to use the new Post object and it's done ! Thanks to #daremachine for his help :)

RESTful API: Data from multiple endpoints in one single view

I've started creating a RESTful API (well, I did my best, I'm trying to follow the patterns) and I have stumbled upon a scenario that I'm not really sure how to handle. I will explain the current structure:
My application has 4 controllers:
Customers
Payments
Log
Taking as example the Customers controller, I have defined the following actions:
GET /customers: returns a list of customers
POST /customers: creates a new customer
GET /customers/{id}: returns the customer with the provided id
PUT /customers/{id}: updates the customer with the provided id
DELETE /customers/{id}: destroys the customer
This is the full code of the Customer controller:
namespace App\Http\Controllers;
use App\Customer;
use Illuminate\Http\Request;
class CustomerController extends Controller
{
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
return Customer::all();
}
/**
* Store a newly created resource in storage.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$customer = Customer::create($request->all());
return response()->json($customer, 201);
}
/**
* Display the specified resource.
*
* #param \App\Customer $customer
* #return \Illuminate\Http\Response
*/
public function show(Customer $customer)
{
return $customer;
}
/**
* Update the specified resource in storage.
*
* #param \Illuminate\Http\Request $request
* #param \App\Customer $customer
* #return \Illuminate\Http\Response
*/
public function update(Request $request, Customer $customer)
{
$customer->update($request->all());
return response()->json($customer, 200);
}
/**
* Remove the specified resource from storage.
*
* #param \App\Customer $customer
* #return \Illuminate\Http\Response
*/
public function destroy(Customer $customer)
{
$customer->delete();
return response()->json(null, 204);
}
}
The code is very similar in the other controllers. It's also important to note that:
A Customer can have multiple Payments
A Customer can have multiple records in the Log
The problem starts here:
I need to display in the front-end a summary page with all customer data (name, email, registration date, etc) and a box showing the number of payments made and another box showing the number of entries in the Log.
Do I need to make 3 requests? (One to /customers/id, other to customers/id/payments and other to customers/id/logs)
If I return all the customer related data in the customers/id call, am I breaking the RESTful convention?
I am using apigility, but my answer still will be related to your question. According to the REST terminology (which could be find here https://apigility.org/documentation/intro/first-rest-service#terminology ) You are talking about entity and collection.
/customers/id - entity,
/customers/id/payments - collection,
/customers/id/logs - collection.
These are 3 different requests. So, yes, you need make 3 different requests.
But, to be honest, if you don't need pagination over payments and logs you can have only one request to /customers/id and within response you can have fields with array
{
"_links": {
"self": {
"href": "http://localhost:8080/status/3c10c391-f56c-4d04-a889-bd1bd8f746f0"
}
},
"id": "3c10c391-f56c-4d04-a889-bd1bd8f746f0",
...
_payments: [
...
],
_logs: [
...
],
}
Upd (duplicate from comment for future visitors).
Also, you should pay attention to DTO. I suppose this link will be interesting https://stackoverflow.com/a/36175349/1581741 .
Upd2.
At current moment I treat your collection /customers/id/payments like this:
/payments?user_id=123
where user_id is filtering field on payments table.
I think your problem that you confuse your REST API with your database. They don't have to follow the same structure. You can easily return the whole nested JSON for GET /customers/{id} if that's what you need from your REST API.

Laravel: How to use scope with mass assignment

I have two entities with many to many relationship: Form and Section. A valid section must to be "active" then I have a scope:
/**
*
* #param Builder $query
* #return Builder
*/
public function scopeActive($query)
{
return $query->where('is_active', true);
}
And when rendering the form, just pass the active sections to the view:
$sections = Section::active()->get();
The problem is that when the POST request is sent, I have to perform a mass assignment in relationship with sync and have not control over the id's passed (actives and not actives):
$form->sections()->sync($sectionsId);
There is a way to attach only active sections without manually checking each one?
You have to take sections in $sectionIds first and then you have to use sync() method.
Try this code,
$sectionIds = Section::active()->whereIn('id', [1, 2, 3])->pluck('id');
$form->sections()->sync($sectionIds);
I believe that this will work.

Passing a object from view to controller with typo3 6.2.9

I faced the following problem in Typo3 6.2.9:
When I pass some objects to the controller by using f:link action and arguments, the objects will arrive at the controller but he does not register them as they what I have send. For a better explanation here the single code sections:
at the partial:
<f:link.action action="new" controller="ForeignProductMeasuring" arguments="{planRow : planRow, plan:plan}">neue Dosierung anlegen</f:link.action>
the generated link in the site:
"index.php?id=1&tx_desinfektionsplan_desinfektionsplan%5BplanRow%5D=12&tx_desinfektionsplan_desinfektionsplan%5Bplan%5D=16&tx_desinfektionsplan_desinfektionsplan%5Baction%5D=new&tx_desinfektionsplan_desinfektionsplan%5Bcontroller%5D=ForeignProductMeasuring&cHash=2adafaba9c56b134d4d5ae382ee5d57b"
what shows the the arguments are correct in my opinion.
Here the action at the controller:
public function newAction(\Orochemie\Desinfektionsplan\Domain\Model\ForeignProductMeasuring $newForeignProductMeasuring = NULL,
\Orochemie\Desinfektionsplan\Domain\Model\PlanRow $planRow,
\Orochemie\Desinfektionsplan\Domain\Model\Plan $plan) {
\TYPO3\CMS\Extbase\Utility\DebuggerUtility::var_dump( $this->request->getArguments());
$this->view->assign('newForeignProductMeasuring',$newForeignProductMeasuring);
$this->view->assign('planRow', $planRow);
$this->view->assign('plan', $plan);
}
The error looks like this:
#1: PHP Catchable Fatal Error: Argument 2 passed to
Orochemie\Desinfektionsplan\Controller\ForeignProductMeasuringController::newAction()
must be an instance of Orochemie\Desinfektionsplan\Domain\Model\PlanRow,
none given in /kunden/137629_70806/typo3/typo3conf/ext/desinfektionsplan/Classes/Controller/ForeignProductMeasuringController.php line 46
For me it looks like that he doesn't knows the objects which he gets. But if I check the arguments with $this->request->getArguments() then I see that all arguments arrive at the controller.
Does anybody has a idea of what is the problem here?
Did you define your params in the PHPDoc annotations?
/**
* #param \Orochemie\Desinfektionsplan\Domain\Model\ForeignProductMeasuring newForeignProductMeasuring
* #param \Orochemie\Desinfektionsplan\Domain\Model\PlanRow $planRow
* #param \Orochemie\Desinfektionsplan\Domain\Model\Plan $plan
* #return void
*/
public function newAction(\Orochemie\Desinfektionsplan\Domain\Model\ForeignProductMeasuring $newForeignProductMeasuring = NULL,
\Orochemie\Desinfektionsplan\Domain\Model\PlanRow $planRow,
\Orochemie\Desinfektionsplan\Domain\Model\Plan $plan) {
...
}
These are pretty important for the ObjectMapper.

Explanation of how symfony2 annotations work

I am using symfony2 annotations and want to know how cascading works in this format.
Lets say I have:
/**
* #Route("/reviews/{slug}", name="reviewDetail")
* #Template()
*/
first, then I check to see if that pulls any data. If not, I do a redirect to the following controller using the following redirect:
return $this->redirect($this->generateUrl('reviewsDate', array('date' => $slug)), 301);
which should go to:
/**
* #Route("/reviews/{date}", name="reviewsDate", defaults={"date" = null})
* #Template()
*/
then check to see if that pulls any data and, if not, create a fallback to this using a redirect:
/**
* #Route("/reviews", name="reviews")
* #Template()
*/
When I run a redirect:
if ($ctx->getReview($slug)) {
$review = $ctx->getReview($slug);
} else {
return $this->redirect($this->generateUrl('reviewsDate', array('date' => $slug)), 301);
}
I get this error:
This webpage has a redirect loop
The actions are all stacked in the order of acceptance, so I would check for the slug first, then the date, then if no result, kick it to the main reviews page.
I can change the route to be more specific, which would work, but it seems not as user friendly. For instance, if I wanted to have these multiple routes:
reviews/my-review: shows the specific review
reviews/2014: shows all reviews from the 2014 year
Is this the wrong way of of executing this functionality?
/**
* #Route("/reviews/{date}", name="reviewsDate", defaults={"date" = null})
* #Template()
*/
this is this same route as
/**
* #Route("/reviews", name="reviews")
* #Template()
*/
becouse you have default value null for date parameter so if you redirect to reviews you are going to reviewDate without parameter that causes endless loop.

Categories