I'm planning to organize my controllers in sails using subfolder but I'm not sure how to do it. When I tried using like admin/PageController.js and connect it with the route I keep getting a 404 error.
You can definitely do this. The trick is, the controller identity is its path, in your case admin/PageController. So a custom route in config/routes.js would be something like:
'GET /admin/page/foo': 'admin/PageController.foo'
The great thing is, automatic actions still work, so if you have an index action in the controller then browsing to /admin/page will automatically run it.
You can also create controllers like this with sails generate controller admin/page.
Edit
Since commit 8e57d61 you can do this to get blueprint routes and functionality on nested controllers, assuming there is an AdminPage model in your project:
// api/controllers/admin/PageController.js
module.exports = {
_config: {
model: 'adminpage'
}
}
or this:
// config/routes.js
module.exports.routes = {
'admin/page': {
model: 'adminpage'
}
}
Old Answer
Your options
Defining explicit routes to your grouped controllers in config/routes.js.
Look at Scott Gress' answer for more details.
(If you are a bit adventurous) As i had the exact same requirement for a project of mine I created a Pull Request on Sails that allows you to override the model - controller association. You could install it via
npm install -g git://github.com/marionebl/sails.git#override-controller-model
Assuming it is the api/models/Page.js model you want the blueprint methods for on api/controllers/admin/PageController.js you then could do:
// api/controllers/admin/PageController.js
...
module.exports = {
_config: {
model: 'page'
}
}
Explanation
While generating/creating grouped controllers like this is perfectly valid an possible, you will not get the default blueprint routes you'd expect for controllers accompanied by models with the same identity.
E.g. api/controllers/UserController.js and api/models/User.js share the same identity user, thus the blueprint routes are mounted if they are enabled in config/blueprints.js.
In fact at the moment it is not possible to group models into subfolders in a valid way. This means you won't be able to create a model that matches the identity admin/page of your controller api/controllers/admin/PageController.js - the blueprint routes are not mounted for PageController.
The source responsible for this behavior can be inspected on Github.
I made a diagram that shows how implicit routes, explicit policies, nested controllers, singular models and nested views are related. It does not show an overridden model-controller association as described by #marionebl.
It was mostly an exercise for me to understand this topic better, but I hope it helps somebody else too. Please let me know if I made any mistakes:
Thanks merionebl, its work fine for me and I want to share with all guys my answer derived from merionebl answer.
/config/routes.js
'get /admin/user' : {
controller: "Admin/UserController", action: "find",
model : 'user',
},
My aim is not repeat answer just have upgrade and clear example.
Thanks
Related
I apologize for what is likely a very amateur question. Coming from Laravel, this is still quite confusing to me and I just don't understand what is needed.
I have a user.repository.ts file and a location.repository.ts file. Each have their own modules, controllers and services. I have successfully created CRUD operations for each entity but now am trying to work towards a Many to Many relationship.
In my user.repository.ts file, I am trying to save a related (many to many) repository:
// user.repository.ts
user.locations = await this.locationRepository.findByIds(locations);
...
await user.save();
I am not sure how to inject or import the location.repository.ts file. I have tried numerous variations of importing the service into each module. Or importing each module into the other module. I have tried different versions of this:
#EntityRepository(User)
#EntityRepository(Location)
Or importing the LocationService into the UserService.
In Laravel, this would be as "simple" as $model->sync($relationship);
How can I import/inject the locationRepository into my userRepository? Thank you for any suggestions!
I assume this question is related to your last question, the simplest way to implement it, Add Locationentity to your UserModule
#Module({
imports:[TypeOrmModule.forFeature([UserRepository,LocationRepository])], // or just Location entity [UserRepository,Location] if you didn't setup a custom LocationRepository
After that, inject it in your as what you did for userRepo... service
constructor(
#InjectRepository(LocationRepository)
private locationRepository: LocationRepository,
// or just private locationRepository: Repository<Location>,
) {}
In the create method service get your locations:
async createUser(createUserDto: CreateUserDto) { // usersSrvice
let locations = await this.locationRepository.findByIds(createUserDto.locations);
await this.userRepository.createMethodeName(createUserDto,locations) // add secand
params for your locations
don't hesitate to ask if you have any other questions
What is the best way to define custom functions in loopback api that can be used in models defined ?
For example a basic express application can have functions in helper folder on root directory, but doing same in loopback is not recommended and does not maintain loopback way.
Any help would be highly appreciated.
This is very well documented.
Custom logic can be placed in
boot scripts
middlewares
models:
remote methods
remote hooks
operation hooks
application-decoupled logic can very well be put in helper folders, separate folders at root level, etc. There is nothing wrong with modularizing your program this way, it is actually a good thing.
As mentioned by others, the Loopback documentation answers your question like this:
Adding logic to models - adding remote methods, remote hooks and operation hooks.
Defining boot scripts - writing scripts (in the /server/boot directory) that run when the application starts.
Defining middleware - adding custom middleware to the application .
That's a great answer if you have custom functions for a particular model, boot script, or middleware. And as Dharmendra Yadav said, mixins can be another option:
You can use mixins to perform different common actions on models such as observing changes using operation hooks and adding model attributes.
But what about code that simply doesn't fit into any of those categories?
I don't have experience with a lot of web frameworks, but one framework I have used is Grails, which is very opinionated and gives you a place for just about everything. And if your code doesn't fit into any of those categories, they give you a place for that too:
src/main/groovy - Supporting sources
So when I ran into this same problem in Loopback, I just created a src directory under server and that's where I put some helper classes that don't seem to fit anywhere else. And I include them as needed:
const HelperClass = require('../src/HelperClass');
HelperClass.helperFunction()...
Of course you can name the folder however you'd like: src, helpers, support, whatever. And then put it under common or server as appropriate.
The best way to define functions in loopback i found is to use mixins. Here is the sample way of doing so..
https://loopback.io/doc/en/lb3/Defining-mixins.html
You can inherit these defined mixins into your models through .json of your model with {mixins:yourmixin.js}, nice and easy.
Here's some sample code to get you headed in the right direction.
mynewmodel.js
http://domain/api/mynewmodel/myfunc.js
function myfunc(data, callback) {
// ** Put your code here **
console.log('myfunc');
}
function remoteMethod(model) {
model.remoteMethod(
'myfunc',
{
http: { verb: 'post' },
accepts: { arg: 'data', type: 'data' },
returns: [
{ arg: 'returndata', type: 'data' }
]
}
)
}
UPDATE
Generally your js files go in common/models
Are attribute-based routes supported by T4MVC in some way?
I have applied a RoutePrefixAttribute to my MVC 5 controller and a Route attribute on my action. T4MVC, as it stands, does not seem to provide routes based on these attributes. The route that it provides is the convention-based routes of /Area/Controller/Action.
Folder structure is:
/
Areas
Ratio
Controllers
RatioSet
PresetGroupController.cs
Views
RatioSet
GroupDetails.cshtml
Controller:
[RoutePrefix("Ratio/RatioSet/Preset/Group")]
public partial class PresetGroupController
{
[Route("Details")]
public virtual ActionResult Details()
{
//.....
return View(MVC.Ratio.RatioSet.Views.GroupDetails, model);
}
}
Now, if I try the following:
return RedirectToAction(MVC.Ratio.PresetGroup.Details());
I get a 404 error, because the requested URL is:
<app_root>/Ratio/PresetGroup/Details
which is the "default" route, rather than the correct attribute-specified:
<app_root>/Ratio/RatioSet/Preset/Group/Details
So, does T4MVC only work with the convention-based routes, inferred from the folder structure, and not any routes that are specified via attributes?
I know this is an old question, but I had the same problem and ended up fixing it adding the RouteArea attribute to the Controller.
Something like this:
[RouteArea("Ratio")]
[RoutePrefix("Ratio/RatioSet/Preset/Group")]
public partial class PresetGroupController
{
[Route("Details")]
public virtual ActionResult Details()
{
//.....
return View(MVC.Ratio.RatioSet.Views.GroupDetails, model);
}
}
Have you verified that the non-T4MVC equivalent is working? If so, what does that line look like?
Note that T4MVC really doesn't generate routes itself, but instead calls into standard MVC framework methods to do this. See section 1.1 in the docs.
My guess is that you're running into an issue that is unrelated to T4MVC. e.g. see this issue where the problem is the order of registration calls.
How can I hide a method for a related model?
Let's say that, in the demo app loopback-example-datagraph, I don't want to expose the DELETE /customers/{id}/orders method.
How should I go about this?
For loopback 1.x, the relation is mapped to a prototype method internally. To not expose it as REST APIs, try the following:
var customer = app.models.Customer;
customer.prototype.__delete_orders.shared = false;
Disclaimer I Have Never Used StrongLoop
Wild stab but it looks like this might work. When you add a relation it adds a method to the underlying model class. when you add a has many it adds this method
customer.orders.destroyAll(function(err) {
...
});
source: http://docs.strongloop.com/display/DOC/Creating+model+relations#Creatingmodelrelations-Methodsaddedtothemodel.1
You should be able to say something like
var customer = app.models.Customer;
customer.orders.destroyAll.shared = false;
I want to display completely different layouts for users in different roles on the root url of my application. I am currently achieving this using the following lines in bootstrap.php.
if (Auth::instance()->logged_in()){
Route::set('default', '(<controller>(/<action>(/<id>)))')
->defaults(array('controller' => 'profile','action' => 'index',));
}
else{
Route::set('default', '(<controller>(/<action>(/<id>)))')
->defaults(array('controller' => 'welcome','action' => 'index',));
}
What is the best practice to achieve this in Kohana? Is it ok to add more lines for different roles in bootstrap.php.
Thanks
you should consider using lambdacallback-route-logic
If allows you to modify the requested URL dynamically and much more cleaner than writing something in bootstrap.php
Why dont change basic template in ONE controller (and using the same route)? I think, your controller code doesn't differs if user logged in or not.
I do it like this:
Create an abstract class Controller_Rolebased where in before() method you can implement Role checking.
And then for example:
class Controller_Profile extends Controller_Rolebased
{
protected $_accept_roles = array('user', 'admin'); // this array Controller_Rolebased class will use in before method.