How to execute the right route? symfony - backbone.js

Im using backbone, and i want to save a new model into database with collection.create() method.
I have two routes with the same name /document in Symfony. The two routes have different methods first route for querying for objects and second route for saving objects into database. So how can i specify the route for saving object to database? Is it possible?
Here si my controller, but symfony is creating for me router for documentAction() method and for createAction method. Because when i write in console to see my names of router with php bin/console debug:router i see that it's made by symfony.
<?php
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse;
use AppBundle\Entity\Document;
use Doctrine\ORM\Mapping as ORM;
class DocumentQuery extends Controller
{
/**
* #Route("/doc")
* #Method({"GET"})
*/
public function documentAction()
{
$id = $this->getUser()->getId();
$em = $this->getDoctrine()->getManager();
$documents = $em->getRepository('AppBundle:Document')->findAllOrderedById();
$titles = array();
foreach($documents as $document)
{
$user_id = $document['user_id'];
$title = $document['title'];
if($user_id == $id)
{
$titles[] = $document;
}
}
return new JsonResponse($titles);
//return new JsonResponse($document);
//return new JsonResponse($documents);
//dump(json_encode($documents));
//return $this->render('default/doc.html.twig', array('documents' => $documents, 'id' => $id));
}
/**
* #Route("/doc")
* #Method({"POST"})
*/
public function createAction(Request $request)
{
$content = $request->getContent();
$document = new Document();
$document->setTitle($request->get('title'));
//$document->setBody($content);
$em = $this->getDoctrine()->getManager();
$em->persist($document);
$em->flush();
return new JsonResponse($document);
}
}
Collection:
var User = Backbone.Collection.extend({
model: DocumentUser,
url: '/doc',
initialize: function(){
this.fetch();
console.log(this, "this.collection");
},
});
Collection.create:
save: function()
{
this.collection.create({
title: $("#title").val()
});
console.log(this.collection);
}
Routing yml. I made it by your posted code:
app:
resource: "#AppBundle/Controller/"
type: annotation
document_query:
path: /doc
defaults: { _controller: AppBundle:Controller:DocumentQuery:documentAction}
methods: [GET]
document_save:
path: /doc
defaults: { _controller: AppBundle:Controller:DocumentQuery:createAction}
methods: [POST]
Here is the router, you can see app_documentquery_document and app_documentquery_create is made by symfony, and the document_query and document_save i made in routing.yml

Related

How to send notifications to Laravel Users with Onesignal notification channel?

I'm trying to use laravel-notification-channels/onesignal with Laravel 9, Inertia React project.
For first I setup the client in this way:
useEffect(() => {
OneSignal.init({
appId: "PRIVATE-KEY"
});
}, []);
Testing from Onesignal panel the client is listening.
For the back-end I have created a Notification:
<?php
namespace App\Notifications;
use NotificationChannels\OneSignal\OneSignalChannel;
use NotificationChannels\OneSignal\OneSignalMessage;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Notifications\Messages\BroadcastMessage;
use App\Models\Order;
class OrderPlacedNotification extends Notification
{
use Queueable;
public $order;
/**
* Create a new notification instance.
*
* #return void
*/
public function __construct(Order $order)
{
$this->order = $order;
}
/**
* Get the notification's delivery channels.
*
* #param mixed $notifiable
* #return array
*/
public function via($notifiable)
{
return ['database', 'broadcast', OneSignalChannel::class];
}
/**
* Get the array representation of the notification.
*
* #param mixed $notifiable
* #return array
*/
public function toArray($notifiable)
{
return [
'order' => $this->order,
];
}
public function toBroadcast($notifiable)
{
return new BroadcastMessage([
'order' => $this->order
]);
}
public function toOneSignal($notifiable)
{
return OneSignalMessage::create()
->setSubject("Nuovo ordine!")
->setBody("Vedi l'ordine.")
->setUrl('http://onesignal.com');
}
}
and I send the notification via controller to all users.
All config setted but I can't listen to the user.
I found the solution. In my client I subscibe user to the specific interest and in backend I send notifictions at the users with that specific interest:
Frontend
useEffect(() => {
if(auth.user) {
window.Echo.private(`App.Models.User.${auth.user.id}`).notification(notification => {
console.log(notification);
setNotifications(() => [...notifications, notification]);
})
OneSignal.init(
{
appId: "KEY",
},
//Automatically subscribe to the new_app_version tag
OneSignal.sendTag("orders", "orders", tagsSent => {
// Callback called when tag has finished sending
console.log('TAG SENT', tagsSent);
})
);
}
}, [notifications]);
User Model:
public function routeNotificationForOneSignal()
{
return ['tags' => ['key' => 'orders', 'relation' => '=', 'value' => 'orders']];
}

Render AngularJs index.html in an ASP.NET Core MVC view

I'm currently working on migrating a web app with the following tech:
ASP.NET MVC
AngularJs
Gulp
Azure cloud service
to :
ASP.NET Core MVC
AngularJs
Webpack
Azure App service
The migrated app correctly bundle and serve AngularJs index.html to wwwroot.
Currently, I need to add two views to the ASP.NET Core MVC app and inject the index.html to these views.
I can't figure out how to :
Inject the index.html
Make one of the views as a startup view.
to finally have an URL pattern:
localhost/View1/#/angularJs-state
or
localhost/View2/#/angularJs-state
wwwroot :
Home :
public class BaseController : Controller
{
public IActionResult Index()
{
return View("portal");
}
}
First view :
public class PortalController : Controller
{
public IActionResult Index()
{
return View();
}
}
Startup.cs
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(name: "portal",
pattern: "Portal",
defaults: new { controller = "Portal", action = "Index" });
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Base}/{action=Index}/{id?}");
});
The first view is correctly displayed but without the view name in
the url .
Is it possible to render the index.html from wwwroot in the cshtml
view using #html.Partial ?
According to your description, if you want to render the html page in the view, I suggest you could write a custom render method in mvc and return the index file as the file result to the mvc view.
More details, you could refer to below codes:
1.Create render extension method:
public static class HtmlHelperViewExtensions
{
public static IHtmlContent RenderAction(this IHtmlHelper helper, string action, object parameters = null)
{
var controller = (string)helper.ViewContext.RouteData.Values["controller"];
return RenderAction(helper, action, controller, parameters);
}
public static IHtmlContent RenderAction(this IHtmlHelper helper, string action, string controller, object parameters = null)
{
var area = (string)helper.ViewContext.RouteData.Values["area"];
return RenderAction(helper, action, controller, area, parameters);
}
public static IHtmlContent RenderAction(this IHtmlHelper helper, string action, string controller, string area, object parameters = null)
{
if (action == null)
throw new ArgumentNullException(nameof(controller));
if (controller == null)
throw new ArgumentNullException(nameof(action));
var task = RenderActionAsync(helper, action, controller, area, parameters);
return task.Result;
}
private static async Task<IHtmlContent> RenderActionAsync(this IHtmlHelper helper, string action, string controller, string area, object parameters = null)
{
// fetching required services for invocation
var currentHttpContext = helper.ViewContext.HttpContext;
var httpContextFactory = GetServiceOrFail<IHttpContextFactory>(currentHttpContext);
var actionInvokerFactory = GetServiceOrFail<IActionInvokerFactory>(currentHttpContext);
var actionSelector = GetServiceOrFail<IActionDescriptorCollectionProvider>(currentHttpContext);
// creating new action invocation context
var routeData = new RouteData();
var routeParams = new RouteValueDictionary(parameters ?? new { });
var routeValues = new RouteValueDictionary(new { area, controller, action });
var newHttpContext = httpContextFactory.Create(currentHttpContext.Features);
newHttpContext.Response.Body = new MemoryStream();
foreach (var router in helper.ViewContext.RouteData.Routers)
routeData.PushState(router, null, null);
routeData.PushState(null, routeValues, null);
routeData.PushState(null, routeParams, null);
var actionDescriptor = actionSelector.ActionDescriptors.Items.First(i => i.RouteValues["Controller"] == controller && i.RouteValues["Action"] == action);
var actionContext = new ActionContext(newHttpContext, routeData, actionDescriptor);
// invoke action and retreive the response body
var invoker = actionInvokerFactory.CreateInvoker(actionContext);
string content = null;
await invoker.InvokeAsync().ContinueWith(task =>
{
if (task.IsFaulted)
{
content = task.Exception.Message;
}
else if (task.IsCompleted)
{
newHttpContext.Response.Body.Position = 0;
using (var reader = new StreamReader(newHttpContext.Response.Body))
content = reader.ReadToEnd();
}
});
return new HtmlString(content);
}
private static TService GetServiceOrFail<TService>(HttpContext httpContext)
{
if (httpContext == null)
throw new ArgumentNullException(nameof(httpContext));
var service = httpContext.RequestServices.GetService(typeof(TService));
if (service == null)
throw new InvalidOperationException($"Could not locate service: {nameof(TService)}");
return (TService)service;
}
}
2.Add controller method as below:
public IActionResult IndexFile()
{
return File("index.html", "text/html");
}
3.Add below codes into view:
#Html.RenderAction("IndexFile", "Yourcontrollername")

Angular - Display Dashboard and other component.html based on user_id

Am unable to display component.html and dashboard based on user_id
I am using Angular to connect to Laravel endpoints. I have User model and other models. Every other tables have user_id in it. There are two dashboards: admin-dashboard and user-dashboard.
Laravel
public function login()
{
$credentials = request(['email', 'password']);
if (!$token = auth()->attempt($credentials)) {
return response()->json(['error' => 'Email or Password doesn\'t exisit'], 401);
}
return $this->respondWithToken($token);
}
models for user, student and assignment:
protected $fillable = [
'name', 'email', 'password', 'username',
];
protected $fillable = [
'name', 'class', 'user_id',
];
protected $fillable = [
'title', 'valid_date', 'content', 'user_id',
];
Angular: Service
private baseUrl = 'http://localhost/cloudengine-sandbox/cloudsandboxbackend/public/api';
//private baseUrl = '/api';
constructor(private http:HttpClient) { }
register(data){
return this.http.post(`${this.baseUrl}/register`, data)
}
login(data){
return this.http.post(`${this.baseUrl}/login`, data)
}
login.component.ts
onSubmit() {
this.Jarwis.login(this.form).subscribe(
data => this.handleResponse(data),
error => this.handleError(error)
);
}
handleResponse(data){
this.Token.handle(data.access_token);
this.Auth.changeAuthStatus(true);
//this.user.loggedInUser = user;
this.router.navigateByUrl('/admindashboard');
}
When I login:
If user_id is 1, it should redirect to admindashboard else userdashboard
The admin should see everything in the application, while other users should see the components/forms that its table has the user_id of that particular user
Just add a if check inside the handleResponse method,
handleResponse(data){
if(this.user.user_id == 1){
this.router.navigateByUrl('/admindashboard');
}
else
{
this.router.navigateByUrl('/userdashboard');
}
}
better way to do this should be using a Using Route Guards.

How do I create a custom object class that's available to my methods in AngularJS

I'm a huge fan of angular but it's got some tricky concepts with extremely nuanced differences between them and this is one of them.
I just want to create an class that I can use to create custom objects in my Angular controllers and factories. It surely shouldn't be that hard but I can't figure out how to do it. I want to have a custom, ResultSet class which I can instantiate to create instances of ResultSet. However for the life of me I can't figure out the correct syntax of factory v. service to use.
This is all I want:
ResultSet = function(dataSet){
this.filter = function(){
# filters and returns dataSet
# ...
}
}
and then I want to be able instantiate an instance of ResultSet inside a controller etc:
MyApp.controller('pageCtrl', ['ResultSet', (ResultSet) ->
# ...
rs = ResultSet.new(dataToFilter)
How can I create a service that allows me to create instances of my custom object?
It seems more correct to use an Angular Service rather than a Factory since a service returns an instance of an object (which is exactly what I want). But I can't figure out how to do this...
How would I use a service to declare my custom ResultSet class and then how would I instantiate an instance from it?
Maybe you were looking for something like this:
.factory('User', function (Organisation) {
/**
* Constructor, with class name
*/
function User(firstName, lastName, role, organisation) {
// Public properties, assigned to the instance ('this')
this.firstName = firstName;
this.lastName = lastName;
this.role = role;
this.organisation = organisation;
}
/**
* Public method, assigned to prototype
*/
User.prototype.getFullName = function () {
return this.firstName + ' ' + this.lastName;
};
/**
* Private property
*/
var possibleRoles = ['admin', 'editor', 'guest'];
/**
* Private function
*/
function checkRole(role) {
return possibleRoles.indexOf(role) !== -1;
}
/**
* Static property
* Using copy to prevent modifications to private property
*/
User.possibleRoles = angular.copy(possibleRoles);
/**
* Static method, assigned to class
* Instance ('this') is not available in static context
*/
User.build = function (data) {
if (!checkRole(data.role)) {
return;
}
return new User(
data.first_name,
data.last_name,
data.role,
Organisation.build(data.organisation) // another model
);
};
/**
* Return the constructor function
*/
return User;
})
From this post by Gert Hengeveld.
myApp.factory('ResulSet', function() {
function ResultSetInstance(dataSet) {
this.filter = function(){
// ...
}
}
return {
createNew: function(dataSet) {
return new ResultSetInstance(dataSet);
}
};
});
and then
myApp.controller('pageCtrl', function(ResultSet) {
var someData = ...;
var rs = ResultSet.createNew(someData);
}
Edit (from the question asker)
On experimenting with this further I found that you didn't even need to have the createNew method.
myApp.factory('ResultSetClass', function() {
ResultSetClass = function(dataSet) {
this.filter = function(){
// ...
}
}
return ResultSetClass
});
works just fine and then you can call new ResultSetClass(args).
Note for those using Coffeescript
Coffeescript will return the last variable or method in your class instance so if you are using coffeescript (as a general rule), it's imperative to return this at the end of the class definition
myApp.factory 'ResultSetClass', () ->
ResultSetClass = (dataset) ->
this.filter = () ->
# do some stuff
return this
return ResultSetClass
If you don't return this explicitly then you'll find that when you call
myApp.factory 'ResultSetClass', () ->
ResultSetClass = (dataset) ->
this.filter = () ->
# do some stuff
then you'll simply be left with the last thing the coffeescript returns which is the filter method.
I recently has do do something like that because I wanted to implement a factory of class instance, and being able to configurate my instances and benefit from Angular Dependency injection. I ended up with something like that
// Implem
export class XAPIService {
private path: string;
/* this DO NOT use angular injection, this is done in the factory below */
constructor(
private seed: XAPISeed,
private $http: ng.IHttpService,
private slugService: SlugService
) {
const PATH_MAP: Map<Y, Z> = new Map([
['x', id => `/x/${id}`],
['y', id => `/y/${id}`],
]);
this.path = PATH_MAP.get(this.seed.type)(this.seed.id);
}
list() {
/* implem that use configured path */
return this.slugService
.from(this.path + `/x`)
.then(url => this.$http.get<IX>(url))
.then(response => response.data)
}
}
export type IXAPIFactory = (s: XAPISeed) => XAPIService;
export function XAPIFactory(
$http: ng.IHttpService,
myService: SlugService
) {
'ngInject';
return (seed: XAPISeed) =>
new XAPIService(seed, $http, myService);
}
// angular
angular.module('xxx', [])
.factory('xAPIFactory', XAPIFactory)
// usage in code
export class XsController implements ng.IComponentController {
/* #ngInject */
constructor(
private xAPIFactory: IXAPIFactory,
) {}
$onInit() {
this.xService = this.xAPIFactory({ id: 'aaabbbaaabbb', type: 'y' });
return this.xService.list()
.then(xs => {
this.xs = xs;
})
}
}

how can I update a model with custom idAttribute

in my simple backbone application, I am trying to update a model and every time it send a put request instead of post.
Well, this is my model named categoryModel
define(['Backbone'], function (Backbone) {
var CategoryModel = Backbone.Model.extend({
defaults: {
ID: '',
Name: 'Empty',
TagID: '0',
GID: '0'
},
idAttribute: "ID",
initialize: function () {
if (!this.get('Name')) {
this.set({ 'Name': this.defaults.Name });
}
}
});
return CategoryModel;
});
this is the collection
define(['Backbone','../../models/categories/categoryModel'], function (Backbone, categoryModel) {
var CategoryCollection = Backbone.Collection.extend({
url: '/parentcategory/Actions',
model: categoryModel
});
return new CategoryCollection;
});
here are my methods in the view
on a keychange event
createNewItem: function (e) {
var $this = $(e.currentTarget);
$('#selectedCategoryName').html($this.val());
//it creates a new model
globals.NewCategory = new CategoryModel({ Name: $this.val() });
}
on handleDrop event
handleDropEvent: function (event, ui) {
var draggable = ui.draggable;
//check if name has set
if (!globals.NewCategory) {
alert("Please write a category name");
$('#createNewCategory').focus();
return;
}
//get itemID
var itemID = draggable.attr("id").split('_')[1];
var itemDesc = draggable.attr("id").split('_')[0];
//check items category
if (itemDesc == "Tag") {
//check if tagID already exists
if (globals.NewCategory.TagID) {
alert("you have already specify a tag from this category");
return;
}
globals.NewCategory.set("TagID", itemID);
} else if (itemDesc == "gTag") {
if (globals.NewCategory.GID) {
alert("you have already specify a tag from this category");
return;
}
globals.NewCategory.set("GID", itemID);
}
categoriesCollection.create(globals.NewCategory, {
silent: true,
wait: true,
success: function (model, response) {
model.set("ID", response);
alert(model.id);
}
});
}
The categoriesCollection.create is called twice. Firstly for setting the TagID (on a success request it gets an ID ) and secondly for setting the GID.
Since the ID has been set, shouldn't had sent a POST request instead of PUT on the second call?
What am I doing wrong?
Thanks
The standard behaviour is to send a POST if the model is new ( doesn't have an ID attributed ) and send a PUT if the model id is set.
In your case it's working as designed, if you want it to use POST to send UPDATES you have to override Backbone.sync to work as you need, but I think it's easier for you to make your backend RESTful and create a PUT listener controller method for updates.
Another thing, if I got it right you are using create() to update models in your collection, I would advise you not to do that and instead use the save() directly in the model you want to update, the code will be a lot more readable.
Cheers.

Resources