Symfony 3.4: How to mock app.request inside twig template - request

Let's say I have the following method
public function prepareEmail($dataModel)
{
return $this->container->get('templating')->render(
'MyBundle:Emails:email-document.html.twig',
[
'data' => $dataModel,
]
)
}
I want to test a very complicated logic that I have in my twig template.
In my phpunit test kit I'm not mocking TwigEngine service so I'm working with a fully rendered templates.
In my test I doing something like this
public function testPrepareEmail()
{
$template = $this->manager->prepareEmail($testData);
static::assertContains('some part of template', $template);
}
Everything looks good, but I'm added the following string to one of my templates
{{ app.request.getSchemeAndHttpHost() }}
And now I'm getting error
Twig_Error_Runtime : Impossible to invoke a method ("getSchemeAndHttpHost") on a null variable.
which is very reasonable, the question is how to mock Symfony Request object in my case?

Related

Get single data request with id or slug on Inertiajs and react code

I am beginner in writing program using Inertiajs, i have been working using both laravel and react js both separately. but in case of Inertia using to combine the two i got stuck in getting a single data from the controller and also to send bunch to the controller from the view
it's hard to understand what's your problem exactly. but as I understand your problem is transferring data between server-side and client-side nodes of your app.
to transfer data from controller to client-side(react component) :
use Inertia\Inertia;
class UsersController extends Controller
{
public function show(User $user)
{
return Inertia::render('Users/Show', [
'user' =>$user,
]);
}
}
and you can get data(user object) in your component's props.
//...
export function Show = ({user})=>{
// access to the user object
}
//...
to transfer data from client-side to server-side use :
import { Inertia } from '#inertiajs/inertia'
Inertia.get(url, data, options)
Inertia.post(url, data, options)
Inertia.put(url, data, options)
Inertia.patch(url, data, options)
Inertia.delete(url, options)
Inertia.reload(options)
for more information see Manual visits

Symfony4 backend with frontend react. best and safest way to pass information?

I am learning React and want to create an application with Symfony4 as my backend and React frontend. I am stuck now when I need to pass some kind of data to the frontend from my backend. I don't really know what is the right way to do it? Following some tutorials I am doing it like this:
From the controller I send some data to the twig file:
/**
* #Route("/")
*/
public function homepage()
{
$date = new DateTime();
$curr_date = $date->format('Y-m-d H:i:s');
return $this->render('base.html.twig', [
'gameDate' => $curr_date
]);
}
In the twig file, I set it as a data-attribute
base.html.twig:
<div id="root" data-event-date="{{ gameDate }}">
Then I can get the variable as a dataset in my JavaScript
App.js:
const root = document.getElementById('root');
ReactDOM.render(<Homepage {...(root.dataset)}/>, root);
And render it from props.
Homepage.js:
class Homepage extends Component {
constructor(props) {
super(props)
this.state = {
prizePool: '',
gameDate: '',
numberOfPlayers: ''
}
}
onParticipateClick = (event) => {
this.setState({prizePool: Math.random()})
}
render()
{
return (
<div className="mt-c-10">
<GameInfoBox gameDate={this.props.eventDate}/>
</div>
)
}
}
This actually works, but I am concerned with showing all the information in data variables because anyone can see it. What if I want to pass user ID or something secret? There should be another way to do it right?
It depend on what you attemps, if you are working on big project, you can use API to serve backend data. Take a look here: https://www.modernjsforphpdevs.com/react-symfony-4-starter-repo/. There is a simple example.
But if you want something more use api-platform or FOSRestBundle.
"Best and safest" is a little ambiguous - do you need strict security, or safe as in code stability etc?
Instead of passing your data from controller to view (twig) and then into HTML elements or global, another way is this:
Controller loads the view file with your nav and other stuff
Controller loads React (however you do this, Webpack etc)
React calls another controller (i.e. fetch()). This controller is probably somewhere like src/Api/Controller/ as it wont render a view so keep it separate to the other controllers which do render a view
The API controller calls your DB or remote API (etc) and gets the data and sends it back as JsonResponse back to React.
React can then show the data, or an error message depending on the response status
The API controller in your MW can also handle errors and do some logging, so React just gets a 200 and the data or a 400 (or whatever) and it can show a nice message to the user as normal.

CakePHP - Adding a JS file to the 'script' block from a controller's beforeRender callback

Is it possible to add a js file to view from a controller beforeRender()? Is it advisable? If not, and if I cannot use a View, should I just use the Event System?
Basically the idea is to have a plugin that I created inject a JS file into 'script' block of the Theme plugin (Separate plugin)
After some more attempts I decided to go thru and use the Event System. I accomplished it this way:
<?php
namespace App\Event;
use Cake\Event\EventListenerInterface;
class ApplicationListener implements EventListenerInterface {
public function implementedEvents() {
return [
'View.beforeLayout' => 'injectJsFile'
];
}
public function injectJsFile(Event $event, $layoutFile){
$event->getSubject()->Html->script('myscript', ['block' => 'script']);
}
}
This does the job for me, but if there is a controller way or a better Event System way, I would love to hear it.

inject breeze service in angularjs + typescript

class Metadata {
constructor(private breeze) {
this.breeze.? // Ctrl+Space Nothing
breeze.? // Ctrl+Space Everything
}
}
angular.module('bs.breeze', ['breeze.angular']);
// do nothing but you could
// create the window.breeze object
angular.module('bs.breeze').run(['breeze', function (breeze) { }]);
//get breeze metadata by hand
angular.module("bs.breeze").factory('metadata', [ 'breeze', (breeze) => new Metadata(breeze)]);
this.breeze.? shows nothing because "private breeze" has none declared type as you can see.
breeze.? shows everything because it does reference to module breeze declared in breeze.d.ts
breeze and this.breeze are the same object
My problem here is how can use AngularJs standards injections like I'm doing in last line when I'm injecting breeze service into metadata service and then when I'm coding Metadata class how can I use "this.breeze" and have TypeScript advantages like IntelliSense.
Finally, it's possible that this.breeze can be seen as breeze module for IntelliSense purposes or exists other way to implement this scenario?.
You can use typeof to refer to the type of a module. In this case since you have the parameter named breeze, you'll need to either rename the parameter or create an import for the global symbol breeze so you can actually refer to it:
import br = breeze;
class MyClass1 {
constructor(private breeze: typeof br) {
}
}
/*** or ***/
class MyClass2 {
constructor(private br: typeof breeze) {
}
}

Cannot load Google charts in Angular App

The below is an excerpt from the controller for the page I am loading (in coffeescript - but should be straightforward to read). Once I have data from the server, I transform it into a json array of arrays. onChartDataReady then runs, loads 3 files via the jsapi all with http status 200, but onGoogleReady is never called.
onDataLoad: (data) =>
# #$scope.chartData = transformed data
#onChartDataReady()
onChartDataReady: =>
google.load 'visualization', '1', { packages: ['corechart'] }
google.setOnLoadCallback #onGoogleReady
onGoogleReady: =>
chartTable = google.visualization.arrayToDataTable #$scope.chartData
chartOptions = { title: 'Some silly data' }
chart = new google.visualization.LineChart document.getElementById('chart_div')
chart.draw chartTable, chartOptions
I copied the quickstart code from google and pasted that into a html file with no controller (i.e. angular is just loading the html) but it has the same problem - callback is not called. When I open the file from my desktop it works fine. Somehow angular is interfering with the google callback and I do not know how to troubleshoot.
Any pointers would be very helpful! Thanks.
This might be related to an odd behavior in the google loader. Try passing the callback inline with the google.load call instead of calling google.setOnLoadCallback. The javascript would look like this:
google.load('visualization', '1', { packages: ['corechart'], callback: <callback function> });
I'm not a coffescript expert, but I think this is the syntax you would use:
google.load 'visualization', '1', { packages: ['corechart'], callback: #onGoogleReady }

Resources