4

I have a CFML ColdBox framework model service which needs to build links. However, models don't have access to the framework SuperType thus don't have access to event.buildLink().

How can I give services within my model the ability to create links? Is there a way to make the buildLink() functionality available through some kind of dependency injection?

Sample model service:

component 
    singleton
{

    function getLinkToUser( required numeric userId ) {

        return event.buildLink( "users.#arguments.usersId#" );

    }

}

--Update--

Many of the comments suggest that embedding framework functionality into the model may be a mistake, and that buildLink() should really only be used within views. For the most part, I agree, and feel introducing framework services into the model violates encapsulation and concerns.

That being said, let's expand the above code example to a more real-world situation: Let's say you have a model service which generates emails to customers and the content of those emails is very much determined by complex business rules. In this case, I could see an argument for generating the email content in the model because that is where business rules live.

If you instead generate the email content in the view, you would be executing business logic into a layer which should really only be used for display/output.

Assuming that generating the email body text in the model layer is the right thing to do, doesn't it also make sense that it should also be able to build HTML links based on framework routes within those emails there as well?

Dave L
  • 3,095
  • 3
  • 16
  • 25
  • 3
    Unfortunately I have not had the time to play around with ColdBox as much as I would like but my gut is telling me that you are doing it wrong. Based on the fact that `buildLink()` is not available to use in the model. It is meant for the view layer. Why can't you use it there instead? – Miguel-F Feb 11 '19 at 21:44
  • Building links in a static class/model using a rewrite rules configuration is a way of doing it, but it is not the way of ColdBox, I guess. – Alex Feb 12 '19 at 01:00
  • 1
    I can't say this would work and am hesitant it's even a good idea since the model should not really know or depend on framework specific functionality that is typically handled in the controller/event, but the ColdBox object that defines buildLink() appears to be "coldbox.system.web.context.RequestContext". In theory, I suppose you could have WireBox create an instance of it and inject it into your service. I have no idea if calling the function with necessary parameters is enough to tap into the usual behind the scenes magic during an event's process; so your mileage may vary. – Tony Junkes Feb 12 '19 at 14:52
  • 2
    @Miguel-F, I think you're right. I shouldn't really have buildLink() within my model. However, I'm trying to create some model services which are used to simplify generating output to views (like a viewlet) or email text. In this particular instance, the string data that I'm generating needs some HTML links. Perhaps the solution is that this type of code belongs in the view layer as opposed to the business/model layer - even though I can see arguments for both cases. – Dave L Feb 12 '19 at 21:29
  • @TonyJunkes I also agree with you. Initially I thought the buildLink() method belonged in a utility/helper class but you're right that it exists as part of the request/event object. – Dave L Feb 12 '19 at 21:32

1 Answers1

1

I recommend using something like CBMailService and then in the send mail you would render the layout/view, and inside of those, you have access to the event to be able to buildLinks.

You can pass in bodyTokens for variables, into the views, and it will handle the rendering for you.

Hope this helps.

var mail = mailservice.newMail(
        to          = arguments.recipients,
        from        = '"Do not reply" <postmaster@noreply>',
        subject     = arguments.emailSubject,
        bodyTokens  = bodyTokens,
        type        = 'html',
        additionalInfo  = { categories: categories }
    );
    mail.setBody(
        renderer.get()
            .renderLayout(
                view    = "/modules/core/views/email/emailSigninSheet",
                layout  = "/modules/core/layouts/email"

            )
    );
Gavin Pickin
  • 742
  • 3
  • 7
  • Good tip, thank you. I will look into CBMailService as a possible solution. My current approach as of today is to have the handler pass along any additional data to the service layer which sounds similar to the way `bodyTokens` works in CBMailService. – Dave L Jan 22 '20 at 22:32