Super User

Super User

星期三, 25 12月 2019 08:24

Joomla中的路由路径

组件访问地址

除了菜单项中设置地址外,组件的访问方式还有:

  • index.php?option=com_name&view=v&layout=l&task=display

  • /compnet/com_name?view=v&layout=l&task=display

各参数的默认值

  • view参数模式是和组件同名,或者在controllerdefault_view制定;
  • layout默认的是default
  • task默认的是根目录controller.php文件中的display

controller

2个controller,根目录下的nameControllercontroller目录下的nameControllermethod

task缺省时访问的是nameController下的displaytask=aaa访问的是nameController下的aaa;

task=method.aaa访问的是nameControllermethod下的aaa函数。

Search engine friendly (SEF)human-readable or clean URLs are URLs that make sense to both humans and search engines because they explain the path to the particular page they point to. Since version 1.5, Joomla! is capable of creating and parsing URLs in any format, including SEF URLs. This does not depend on URL rewriting executed by the web server, so it works even if Joomla! runs a server other than Apache with the mod_rewrite module. The SEF URLs follow a certain fixed pattern, but the user can define a short descriptive text (alias) for each segment of the URL.

这个SEF URL不是说用apache的mod_rewrite模式。而是任何的web服务器都行。

Internally, the local part of a SEF URL (the part after the domain name) is called a route. Creating and processing SEF URLs is therefore referred to as routing, and the relevant code is called arouter.

URL本地的那部分(就是域名后面的那部分)称作route,创建这种URLs叫做routing,相关的代码叫做router。

In Joomla!, each component is responsible for handling its own SEF URLs. Therefore, as the developer of a component, you will have to create your own router to allow your component to use SEF URLs.

 Note: Since Joomla 3.3, one should rather implement the interfaceJComponentRouterInterface and handle the routing and parsing similar as below, but via the methods of this interface. This will also evade the now fixed one way dash-to-colon issue.

The Concept

Assuming you are following standard development practices, your component is probably using "system URLs" that look a lot like http://www.example.com/index.php?option=com_yourcomponent&view=article&id=1&catid=20&Itemid=50, and your goal is to transform this into http://www.example.com/example-menu-item/20/1. As the developer, you have two tasks: signalling the system that certain pieces of text are URLs and need to be transformed, and explaining the system how to transform URLs.

从:http://www.example.com/index.php?option=com_yourcomponent&view=article&id=1&catid=20&Itemid=50

到: http://www.example.com/example-menu-item/20/1

Applying JRoute::_

It is difficult and inefficient for Joomla! to figure out which parts of your component's output are URLs. To support SEF URLs, you will need to change URL-generating code so that it applies JRoute::_before outputting the URL:

echo JRoute::_('index.php?view=article&id=1&catid=20');

Notice that it is possible to leave out the parameters option and Itemidoption defaults to the name of the component currently being executed, and Itemid defaults to the current menu item's ID.

In general, you should only apply this to URLs that users and/or search engines are able to see. For example, there is no need to transform URLs used in redirects that immediately result in other redirects.

If the user turns off SEF URLs in the site's settings, JRoute::_ will produce working non-SEF URLs without any changes to the code.

Writing a router

You'll also need to write a router, which is a single file with two functions that convert system URLs to and from SEF URLs. This file needs to be placed at /components/com_yourcomponent/router.php.

需要一个文件router.php,包含2个函数BuildRoute(编码url)和 ParseRoute (解码url)

The first function, [componentname]BuildRoute(&$query), must transform an array of URL parameters into an array of segments that will form the SEF URL. Schematically, the transformation works as follows:

http://www.example.com/index.php?option=com_yourcomponent&view=article&id=1&catid=20&Itemid=50
↓ JRoute::_, called by your component or any other extension
$query = array('view' => 'article', 'id' => 1, 'catid' => 20)
↓ Your router's com_yourcomponentBuildRoute
$segments = array(20, 1);
↓ Joomla's internal route building (for display)
http://www.example.com/example-menu-item/20/1

The second function, [componentname]ParseRoute($segments), must transform an array of segments back into an array of URL parameters. Schematically:

http://www.example.com/example-menu-item/20/1
↓ Joomla's internal route parsing
$segments = array(20, 1);
↓ Your router's com_yourcomponentParseRoute
$query = array('view' => 'article', 'id' => 1, 'catid' => 20)

The two functions must cooperate in such a way that the original URL can be reconstructed. You can think of BuildRoute as a form of encoding and ParseRoute as the corresponding decoding. When the original URL isn't properly reproduced, your component will stop working.

Preparing Your Data for Routing

Clearly, any URL format needs to contain some kind of information that identifies the data you want to show. If your system URLs look like http://www.example.com/index.php?option=com_yourcomponent&view=article&id=1&catid=20&Itemid=50, that information is currently the id URL parameter (id=1). You probably want your SEF URLs to contain a textual description of the data they point to. In Joomla!, this is usually done by giving your users a way to enter an alias to be used in the URL.

The Alias别名

Even if your users can enter an alias, they might not do so, leaving the generation of a sensible alias up to your component. If your data has a title field, you can use that as a candidate for the alias (like the core Content component does).

在url中文章的id是个重要参数,如果想让url包含一定的语义,最好起个别名。但是,用户可能懒得做,所以还需要系统自生成个别名作为候选。因此别名必须是url安全的,保存前需要

Considering that the alias will be used in URLs, it has to be URL safe. Joomla! provides a method making arbitrary strings URI safe, which includes replacing accented UTF­8 characters by their ASCII­7 equivalents, white spaces by hyphens, etc. Whether the user entered the alias or a candidate has been chosen automatically, you should ensure that the above requirements for a URL safe alias are met. A good place for implementing this, if you are using JTable, is the JTable::check() method, which is called during the save process. Have a look at this example code:

function check()
{
    jimport('joomla.filter.output');
    if (empty($this->alias))
    {
	    $this->alias = $this->title;
    }
    $this->alias = JFilterOutput::stringURLSafe($this->alias);

    /* All your other checks */
    return true;
}

If the alias field is empty the title is used as alias. Then the alias is made URL safe using theJFilterOutput::stringURLSafe() method.

The Slug:id和别用的组合

slug is used to minimise the amount of code you need to support SEF URLs. It consists of the numerical identifier (id) your system URLs used, a colon (:), and the alias you created as described above.

Consider a SEF URL for an Article with id 1 and title "Welcome to Joomla!". The automatically generated alias for this article is welcome-to-joomla, and the slug becomes 1­:welcome­-to­-joomla. In the Content component, the two elements are combined during the database query in the model (a represents #__content):

$query = 'SELECT a.*, '.
         'CASE WHEN CHAR_LENGTH(a.alias) THEN CONCAT_WS(":", a.id, a.alias) ELSE a.id END as slug,'
         /*...*/;

The advantage of this method of creating a slug is that you can simply use the slug as a drop-in replacement for the id in most places. For example, you don't need to check for and remove the colon and the alias from the request data manually: if you use JInput's int (integer) filter, it will do that automatically.

Routing URLs

The JRoute::_ method translates the internal Joomla! URL to a custom URL. JRoute::_ has three parameters and its prototype is:

JRoute::_($url, $xhtml = true, $ssl = null);

Where:

  • $url is a string containing the absolute or relative internal Joomla! URL.
  • $xhtml is a boolean value that specifies whether or not the output should be in XHTML. This parameter is optional and if omitted defaults to true.
  • $ssl is an integer value that specifies whether the URI should be secure. It should be set to 1 to force the URI to be secure using the global secure site URI, 0 to leave it in the same state as when it was passed, and -1 to force the URI to be unsecure using the global unsecure site URI.

The most important parameter is $url. A call to this method might look like:

JRoute::_('index.php?view=article&id=' . $row->slug);

$row-­>slug is the value that was generated in step 2 from a combination of id and title alias.

Another advantage of using JRoute::_ is that the router now handles $option (the component name) and the $Itemid (the menu item ID). The component itself doesn't have to know its name ($option) or the active menu item ($Itemid) like it did in previous version of Joomla!.

It is important that you think about the sequence of the URL parameter in this stage. This will be more clear when we have a deeper look at the router.php in the next section.

The building process of JRouter is divided into two steps:

  • Create the application route. The application route is fully handled by JRouter and the component developer doesn’t have to do anything to make it work.
  • Create the component route. To create the component route, JRouter looks for therouter.php in the component directory which is responsible for building the route for the component.

The Component Router

We will have two functions in the router.php. One is responsible for building the URL and the other is responsible for parsing it. In the next examples, a very basic and a more advanced one, we assume that we have three views that links can point to. The first is a categories overview (view=categories), the second is a single category (view=category) and the third is a single article (view=article).

The file router.php should be in the site area of your component. It is not used on admin/backend pages. Don't forget to add it to your XML manifest file in the site folder.

A Simple Example

This simple example will illustrate the basics of implementing a router for your component.

function [componentname]BuildRoute(&$query)
{
       $segments = array();
       if (isset($query['view']))
       {
                $segments[] = $query['view'];
                unset($query['view']);
       }
       if (isset($query['id']))
       {
                $segments[] = $query['id'];
                unset($query['id']);
       };
       return $segments;
}

JRouter passes a $query array to the [componentname]BuildRoute function. This function will add the relevant parts of the array to the $segments array in the right order and will return the properly ordered array. The content of the $query array needs to be unset, otherwise JRouter will add it to the URL in the form of a query string (i.e. any variables that are not handled by the router will be passed in the query string).

注意一点,$query必须被unset,任何query中的内容最后都会加到route中 

The prefix componentname is the name for your component, as found in the directory holding the component's files. For instance, a component "Magic" in directory /components/com_magic/... would use a prefix magic (all lower case).

The next function in the router.php parses the URL:

function [componentname]ParseRoute($segments)
{
       $vars = array();
       switch($segments[0])
       {
               case 'categories':
                       $vars['view'] = 'categories';
                       break;
               case 'category':
                       $vars['view'] = 'category';
                       $id = explode(':', $segments[1]);
                       $vars['id'] = (int) $id[0];
                       break;
               case 'article':
                       $vars['view'] = 'article';
                       $id = explode(':', $segments[1]);
                       $vars['id'] = (int) $id[0];
                       break;
       }
       return $vars;
}

What happens here? In the function [componentname]BuildRoute we arranged the items in the$query array in a specific sequence. This means that in this example the view is first and the id is second in the array.

By reading $segments[0], we access the name of the view. We set the right view and/or identifier depending on its value and we return the $vars array to JRouter$vars should be an associative array similar to the array that was passed to the BuildRoute method.

The above example of the router.php is a very simple way to generate SEF URLs but should show how this works quite clearly.

The generated URL in this example contains the name of the view and doesn't reflect the content hierarchy:

http://www.example.com/[menualias]/[view]/[slug]

A More Advanced Example

In the next example we will try to get rid of the need for the view and we will try to reflect the current hierarchy level in the URL.

The goal is URL's that look like:

Let's assume we have done step 1 and 2 also for the category.

The link to the article would look like this:

JRoute::_('index.php?view=article&catid=' . $row-­>catslug . '&id='.$row-­>slug);

And the Link to the category would look like this:

JRoute::_('index.php?view=category&id=' . $row->catslug);

The corresponding router.php:

function [componentname]BuildRoute(&$query)
{
       $segments = array();
       if (isset($query['catid']))
       {
                $segments[] = $query['catid'];
                unset($query['catid']);
       };
       if (isset($query['id']))
       {
                $segments[] = $query['id'];
                unset($query['id']);
       };
       unset($query['view']);
       return $segments;
}

The difference now is that we don’t add the name of the view to the $segments array. We still unset the view key since otherwise, JRouter would add it to the URL as part of the query string. Another new thing here is the additional parameter catid that we push into the $segments array.

function [componentname]ParseRoute($segments)
{
       $vars = array();
       $app = JFactory::getApplication();
       $menu = $app->getMenu();
       $item = $menu->getActive();
       // Count segments
       $count = count($segments);
       // Handle View and Identifier
       switch ($item->query['view'])
       {
               case 'categories':
                       if ($count == 1)
                       {
                               $vars['view'] = 'category';
                       }
                       if ($count == 2)
                       {
                               $vars['view'] = 'article';
                       }
                       $id = explode(':', $segments[$count-1]);
                       $vars['id'] = (int) $id[0];
                       break;
               case 'category':
                       $id = explode(':', $segments[$count-1]);
                       $vars['id'] = (int) $id[0];
                       $vars['view'] = 'article';
                       break;
       }
       return $vars;
}

You can see that this ParseRoute function has a lot of different code parts in comparison to the previous. The reason for this is simple. We don’t have the name of the view in the $segments array and we need to find another way to determine it.

We need to find out which level of hierarchy we are in by receiving the root element. We do this by looking to the view name of the active menu item:

$item-­>query['view']

Also we need to know the number of items in the $segments array:

$count = count($segments);

With this information we can correctly set the view for all possible three cases:

  • The menu item is a link to the categories view and the $segments array has two items ($catid and $id). In this case we know that we need to parse a link to an article.
  • The menu item is a link to the categories view and the $segments array has one item ($id). In this case we know that we need to parse a link to a category.
  • The menu item is a link to a category. In this case, we know that any item in the $segmentsarray is the identifier for an article.

The result of all this code is clean and human-readable component URLs.

Routers and Menu Items

A last important part of creating a router is considering what to do with menu items. As explained onSearch Engine Friendly URLs, the output of the component router is used after the first segment of a route, the first segment being the menu item's alias. This creates a difficult question: how is your router and/or other code to know which menu item to route through?

Suppose, for example, that your component is currently producing output for the page /dogs, which lists all dogs in the system. Of course, the items in the list need to be links to pages that display more details about one dog. What should the URL to the dog with ID 21 and name Fido become? Using a router that works according to the principles we've seen so far, the route that is produced is dogs/21-fido, or with some additional work /dogs/fido. But perhaps the user has created a menu item with the alias mydoggy which displays exactly that dog's details. Then it is probably the user's intention to route this URL through that menu item, and the item in the list should link to the page /mydoggy.

More generally, whenever you are building a route, you will need to find the menu item that is most suitable as a starting point for building your route. The term starting point is emphasized because the rest of the route depends on the configuration of the menu item. In our example above, /dogs/21-fido is an acceptable route, /mydoggy is arguably even better, but /mydoggy/21-fido is simply wrong, since /mydoggy is in itself a menu item that is set up to display fido's information.

Several approaches are available to tackle this problem. Joomla!'s core components take a mixed approach, separating responsibilities in two units of code: the router itself and the so-called[componentname]RouteHelper. The [componentname]RouteHelper provides methods that find the most suitable menu item for a given piece of data to be displayed, while the router analyzes the menu item and puts any information that is not determined by the menu item's configuration into the route. This does mean that the calling code must explicitly call the helper's method before routing (echo JRoute::_(DogsRouteHelper::getDogRoute(21));).

星期三, 25 12月 2019 08:17

Joomla经常用到的一些全局函数

  • $doc = JFactory::getDocument(); 返回JDocument实例。

  • $app = JFactory::getApplication(); 返回JApplicationCms实例。

  • $url = JUri::getInstance()->toString(); 获取当前的网址。

  • $user = JFactory::getUser(); 返回JUser实例。

  • JLog::add(内容,级别,目录,日期);

  • $controller = JControllerLegacy::getInstance('$prefix');
    返回prefixController实例。

  • 获取输入url传过来的值JFactory::getApplication()->input->get('task', 'display');。 还可以通过input->get,或者input->post来获取。

  • 跳转,Controller类的setRedirect函数。

  • Controller->getView($vName, $vFormat) 获取视图实例;
    -> setLayout设置layout;
    getModel,然后setModel设置model

星期三, 25 12月 2019 08:04

如何重命名一个Joomla模板

Joomla! 3.6之后不能简单的将模板文件夹改个名就重命名模板了。因为3.6之后模板是当做extension来对待的,所以名字的信息存储在三个地方:模板文件、语言文件和数据库。

安装前

  1. 改模板文件夹名;

  2. templateDetails.xml 文件中:

    <name></name>标签中改过来,<description>标签可改可不改,<language>标签指定的文件名要改;

  3. 语言文件,language 文件夹中将文件名改过来。

安装后

除了以上的那些要改之外,还需要修改数据库。把prefix_extensionsprefix_template_styles这两个表里的名字改过来。

prefix_extensions存储的是模板(扩展)的信息;prefix_template_styles存储的是风格(style)的信息。

星期三, 25 12月 2019 06:40

docker 搭建 LNMP + FTP 环境

需求

搭建个 LNMP 网站环境,想达到以下目的:

  1. 一键安装,不要一个个的安装nginx,php,mysql,然后再手动连接起来;
  2. 方便升级和切换不同的版本
  3. 最好不污染系统环境

我了解的,有三个解决方式:

  1. shell脚本,比如lnmp.org
  2. 各种面板,国外的cPanel,收费;国内的宝塔面板
  3. docker。

最终选择了docker。docker使用起来一点也不复杂,laradock.io上的一句话

Use Docker First - Then Learn About It Later

先使用docker,然后再慢慢了解。docker 架构

安装 docker 和 docker-compose

centos 安装 docker1

移除旧版本
$ sudo yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-engine
安装docker
$ sudo yum install -y yum-utils \
  device-mapper-persistent-data \
  lvm2

$ sudo yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo

$ sudo yum install docker-ce docker-ce-cli containerd.io
启动docker并设置自启动
sudo systemctl start docker
sudo systemctl enable docker
配置镜像,检查docker是否安装成功

官方镜像地址 hub.docker.com 太慢,阿里云有镜像加速,镜像地址每个人不一样,需要登录阿里云账号查看:产品与服务—>弹性计算—>容器镜像加速
阿里云镜像加速
docker 需要 root 权限才能执行,通过将普通用户加入 docker 用户组,可以使用普通用户运行 docker

sudo usermod -aG  docker ${username}

测试 docker 是否安装成功 docker version ,如果没有 server 部分,说明docker安装了没有运行。启动成功后的输出
测试镜像是否设置成功 docker info ,最下面会有 Registry Mirrors
镜像设置成功后的输出

安装 docker-compose2

sudo curl -L "https://github.com/docker/compose/releases/download/1.25.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

sudo chmod +x /usr/local/bin/docker-compose

docker常用命令小全cheatsheet

Dockefile 是创建镜像image
docker-compose.yml 是创建容器的

#### DOCKER#####
docker build -t friendlyname .              #使用当前目录下的Dockerfile创建镜像
docker run -p 4000:80 friendlyname          # 运行"friendlyname" 映射宿主机端口4000到容器端口80
docker run -d -p 4000:80 friendlyname       # 同上,但是以后台模式运行
docker exec -it [container-id] bash         # 进入一个运行中的容器
docker ps                                   # 列出所有运行中的容器
docker stop <hash>                          # 停止指定的容器
docker ps -a                                # 列出所有容器,包括停止运行的
docker kill <hash>                          # 强制停止指定容器
docker rm <hash>                            #删除指定的容器
docker rm $(docker ps -a -q)                # 删除所有容器
docker images -a                            # 列出所有的镜像
docker rmi <imagename>                      #删除指定的镜像
docker rmi $(docker images -q)              # 删除所有的镜像
docker login                                # 登录docker hub
docker tag <image> username/repository:tag  # 给镜像打标签
docker push username/repository:tag         # 上传镜像
docker run username/repository:tag          # 从仓库运行镜像
docker system prune                         # 删除所有未使用的容器、网络、镜像、卷等
docker system prune -a                      # Remove all unused containers, networks, images not just dangling ones (Docker 17.06.1-ce and superior)
docker volume prune                         # 删除所有未使用的卷
docker network prune                        # 删除所有未使用的网络

##### DOCKER COMPOSE#########

docker-compose up                               # 创建并启用容器
docker-compose up -d                            # 以后台模式创建并启用容器
docker-compose down                             # 停止并删除容器、网络和卷
docker-compose logs                             #查看容器的输出
docker-compose restart                          # 重启所有容器
docker-compose pull                             # 拉去所有的镜像服务
docker-compose build                            # 创建所有的镜像服务
docker-compose config                           # 确认并展示 Compose 文件
docker-compose top                              # 显示所有运行的进程
docker-compose run -rm -p 2022:22 web bash      # 运行web服务并以bash作为命令,运行完成后删除

搭建LNMP + FTP 环境

三种方式:

  1. 自己写好dockerfile文件,创建镜像,然后docker run中通过参数连接不同的容器。
  2. 自己写好dockerfile文件,自己写好docker-compose.yml 文件,然后通过docker-compose up来一键生成环境。
  3. GitHub上搜一个LNMPdocker-compose ,然后剪裁一个。

当然是选择最后一种了。docker LNMP这么通用的需求,GitHub上有的是项目。最终选择了laradock3

laradock是为 Laravel 框架创建的 docker-composeLaravel 框架都能用,创建个PHP环境就更没问题了,8.6k的star,2.9k的fork,还有专门的文档网站,值得拥有。

docker-compose up -d nginx mysql phpmyadmin redis workspace 

安装文档的介绍,只需上面的这一条命令,环境就搭建好了。但是,如果此时如果你通过浏览器访问服务器的话,大概率会得到一个nginx的404页面,主要原因是进程的运行用户和文件的访问权限问题。默认情况是nginx进程的UID是82,php-fpm进程的UID是1000。具体操作官网有介绍,我使用中觉得以下几点要注意:

创建用户

创建一个用户,不能登录后台,

sduo useradd -s /sbin/nologin www

此时,在 /home/www 新建public子目录,放入一个index.php文件,然后更改文件权限。因为nginx进程是以UID82访问,所以给予其他用户读和执行的权限

sudo chmod -R a+x /home/www
sudo chmod -R a+r /home/www

裁剪一个docker-compose文件

  1. 生成环境中只把需要的service剪裁处理,nginx,php-fpm,MySQL,phpmyadmin。新建一个.yml文件。

  2. laradock因为是为laravel文件创建的,所以启动php时会自动启动一个workspace 容器和一个docker-in-docker容器,为节省服务器资源,在depend-on和link中去掉。

  3. laradock没有ftp,试了几款,发现stilliard/pure-ftp最好用4,可以指定运行用户和目录。

  4. mysql目录下的my.cnf文件中添加default_authentication_plugin=mysql_native_password ​​​​,否则,很多数据库客户端无法访问。官方还建议宿主机不要对外暴露3306端口,即去掉端口映射port。

  5. .env的主要修改内容有:

    # 宿主机上的网站地址
    APP_CODE_PATH_HOST=/home/www
    
    # 数据存储的位置,比如mysql的数据库数据,ftp的账号数据。
    DATA_PATH_HOST=../lnmpf/data
    
    # 项目名称,容器名的前缀
    COMPOSE_PROJECT_NAME=vdlnmpf
    
    # 设置成www用户的UID,GID
    PHP_FPM_PUID=1000
    PHP_FPM_PGID=1000
    

    还有mysql的密码和ftp的用户密码。
    容器运行后再修改,最好把DATA_PATH_HOST的文件都删掉,否则修改没有作用5

  6. 具体的docker-compose 文件欢迎访问:https://github.com/mengxiangmengyuan/docker-lnmp-ftp


  1. https://docs.docker.com/install/linux/docker-ce/centos/ ↩︎

  2. https://docs.docker.com/compose/install/ ↩︎

  3. https://laradock.io/ ↩︎

  4. https://hub.docker.com/r/stilliard/pure-ftpd ↩︎

  5. https://segmentfault.com/a/1190000017205616 ↩︎

第 2 页 共 2 页

博客标签

© Copyright 2020 zhuameng.com. All Rights Reserved.鲁ICP备16037764号-3

Search