How to create URLs to other applications

Hello Yii dev team (and others),

I have a similar project structure as the one you have on this site with common, frontend and backend. My problem is that I haven’t found a good solution for creating links between the frontend and the backend.

My currently implementation is a custom URL manager which parses the application name from the route and then fetches the application URL from its config. I feel that this isn’t the solution I’m looking for so I’d be very interested to hear how you have solved this problem.

Thanks for reading.

You can start with this:




Yii::app()->request->baseUrl;



Hah, i pose the same question weeks ago and nobody was able to give me an answer.

I case you are interested, this is my current approach:




public static function createFrontendUrl($url, $params=array(), $absolute=false)

    {

        // get the current base url

        $thisBaseUrl = Yii::app()->urlManager->getBaseUrl();

        // find the frontend base url.

        $frontEndBaseUrl = str_replace('/admin', '', $thisBaseUrl);

        // temporary set the base url for frontend.

        Yii::app()->urlManager->setBaseUrl($frontEndBaseUrl);

        // create the url

        if(!$absolute)

            $url=Yii::app()->createUrl($url, $params);

        else

            $url=Yii::app()->createAbsoluteUrl($url, $params);

        // put back the original url

        Yii::app()->urlManager->setBaseUrl($thisBaseUrl);

        // return the generated one.

        return $url;

    }



In the above case, you will have to define your frontend rules into your common configuration so that createUrl() have access to them from backend and from frontend.

actually i met the same problem as Chris83 , then i have to change the backend app as an module :lol: .

your answer is a good tip and customize the UrlManger is also ok . maybe we can do it like this :




                     //in controller :

              $this->createUrl('frontend:someModuleId/someControllerId/someActionId');

 

               /*

                * extends the CUrlManager  and overwrite the createUrl method

                * truncate  the $route string  ,if it has prefix "frontend" then change the baseUrl of                          *request component

*/

             //in  YourUrlManager:


             public function createUrl($route,$params=array(),$ampersand='&')

{

             $currentRequestBaseUrl = Yii::app()->request->baseUrl;

              

              // here  do some thing for different app prefix

              // according to the app name give different baseUrl 

              //  Yii::app()->request->setBaseUrl($MayBeFromConfigOfThisClass);

              $url = parent::createUrl($route,$params,$ampersand);

              //restore the baseUrl

              Yii::app()->request->setBaseUrl($currentRequestBaseUrl);

              return $url;

}  



:D above code didn’t test , just my thought !

That will result in an incorrect URL since the backend will always give its own baseUrl.

Here is my current hack if someone is interested:





<?php

class UrlManager extends CUrlManager

{

	/**

	 * @var array the different application urls (relative)

	 */

	public $appUrls = array();

	/**

	 * @var string $appName the name of the application for which to create url

	 */

	protected $appName;


	/**

	 * Constructs a URL.

	 * @param string $route the controller and the action (e.g. article/read)

	 * It is also possible to specify the application name (e.g. app:article/read)

	 * this is useful when creating urls between applications.

	 * @param array $params list of GET parameters (name=>value).

	 * @param string $ampersand the token separating name-value pairs in the URL. Defaults to '&'.

	 * @return string the constructed URL

	 */

	public function createUrl($route, $params = array(), $ampersand = '&') {

		if (strpos($route, ':') !== false) {

			// If there is a colon in the route we are creating a url to another application

			// and we need to save the application name for later use.

			$matches = array();

			preg_match('/^(.+):/i', $route, $matches);


			if (isset($matches[1])) {

				// Application name found, save it in this manager and remove it from the route.

				$this->appName = $matches[1];

				$route = str_replace($this->appName.':', '', $route);

			}

		}


		return parent::createUrl($route, $params, $ampersand);

	}


	/**

	 * Returns the base URL of the application.

	 * @return string the base URL of the application (the part after host name and before query string).

	 */

	public function getBaseUrl() {

		// When an application name is set we are creating an url to another application

		// and instead of getting the base url from this application we take it from

		// the configuration if available.

		if ($this->appName !== null && isset($this->appUrls[$this->appName])) {

			$baseUrl = $this->appUrls[$this->appName];

		} else {

			$baseUrl = parent::getBaseUrl();

		}


		$this->appName = null; // app name must be reset

		return $baseUrl;

	}

}



With this implementation you can pass the application name in the route:




Yii::app()->createUrl('frontend:model/view', 'id'=>$model->id)



Because it overrides the createUrl method it can be used with any url related component, such as CController::redirect, CWebUser::loginUrl, etc.

How about custom config url rules, how will your hack parse the url rules for frontend while being in backend ?

Say i have a rule like:




controller/x/y/z => 'site/x'



In this case, the hack, will in best case generate, instead of say, /admin/controller/x/y/z the /controller/x/y/z route, which is wrong, because the needed route is /site/x .

In the above example, there is no other way, than to have the rules in the common configuration so that when the url is parsed from backend, the URL manager becomes aware of them and knows to generate the admin/site/x route and later strip the /admin/ part from the generated route(the strip can be done with your hack in this case).

BUT, i still end up sharing url rules between apps, so in this case, why complicate extending the url manager and create urls in the application using something like frontend:/route/here ? This might pose problems when you decide that your hack isn’t good or for any other reason, you decide you want to stop using the hack.

Dunno if you really see my point here, but the fact is that your solution, like mine, doesn’t work as we would like, and there is no way to make it work unless the url rules are shared between the apps.

L.E:

It makes me curious, why when they wrote the wiki article they didn’t touch this issue ?

And also, it would be nice if one of the developers would leave his input here.

Well yes, if your front and back end are on different servers. You were not clear on that.

I actually use it for generating front end URLs, but on same server.

I think the difference between my setup and yours is that I have the different entry scripts in different folders. I have my frontend index.php symlinked directly under the webroot and the backend index.php symlinked to a backend folder under the webroot. I could consider placing the entry scripts in the same folder, it might actually be a better solution.

@twisted1919: Actually, in my implementation the application baseUrl is simply replaced by the pre-configured application URL. It doesn’t even touch the URL before the rules are parsed so it should work fine with the rules as long as they are in the common config which is shared between the frontend and the backend. The reason I chose this approach instead of a method for generating URLs was because it works from both applications and it works with all Yii-components that use array-format URLs directly.

Would anyone on the dev team be kind enough to answer this question?

It’s not what you want to hear, but I use this:


echo CHtml::link('Exit Installer', '../../user/login');

Since the apps are totally separate, I can’t use createUrl.

@jacmoe - well, this is another workaround, which if works and parses the common rules is great.

The thing here is that we need a Yii way of doing this, i know that right now, one is not available, but maybe in the next Yii releases, the developers will address this feature ?

If the apps are separate how on Earth will you do that?

This very site runs two different applications, don’t you ever need to link between them? What I’m looking for is a solid solution (or hack if you will) because we’re creating an application skeleton for large scale applications with a frontend- and backend application. The only missing link is how to link between the two apps.

Not really, no.

When they are separate apps, I seldom have more than two or three links.

I use absolute links or relative links using plain Chtml::link.

You just need a backend-url and a frontend-url variable and then create absolute urls.

The url manager is internal logic.

If you open that up for the world you are messing around with security.

How many links do you need?

You could of course expose an API… but since you probably know in advance what links you are going to use, why not just use them?

:lol: extends the CUrlManager is only suitable for this two apps(frontend and backend) sharing the same url rules , if they have different urlRuls : (index.php?r=someControllerId/someAction) vs (admin.php/someControllerId/someActionId) . so this solution have limitation (generally we just access the frontend from the backend but in contrast , it 's one-way accessing ).

so if there are many yii app beyond the frontend/backend structure , i think we should develop some webservice for these communicated app or use the solution jacmoe give :lol: .

I’m facing the same problem in one of my projects. Thus we will probably convert the backend app to a module.

Here’s another idea (only basic brainstorming):

  • To really have access to all URLs rules you would probably need a configured instance of the other webapp

  • You could thus have an application component which represents the other app (e.g. in your backend app you could have Yii::app()->frontendApp)

  • The component itself would extend CApplicationComponent and act like a wrapper (Proxy) around the frontend application.

  • As Yii usually only allows 1 instance of a webapplication you’d have to use some tricks in this component’s init()

  • You have to override the magic methods to forward any access/calls to the frontend app object. This way the component would behave like a full application object




class AppProxy extends CApplicationComponent

{

    private  _frontendApp;

    public function init()

    {

        // ... read frontend configuration here ...

        $app=Yii::app()->webapp;

        YiiBase::setApplication(null);

        $this->_frontendApp=YiiBase::createWebApplication($config);

        YiiBase::setApplication($app);

    }


    // .. override __get(), __set(), __call() ... here to forward anything to $this->_frontendApp


}

IMO, if you develop two separate applications which depends on each other too much, you are “doing it wrong”. :)

They would be candidates for modules with the same app.

There may be situations where it makes sense. E.g. if the configuration for both is huge and has lots of differences. Also you may want to have the backend app on a different Vhost later. So it really depends.