教你成为全栈工程师(Full Stack Developer) 二十三-番外篇之搜索引擎优化(SEO)

请尊重原创,转载请注明来源网站www.lcsays.com以及原始链接地址

网站结构

搜索引擎分为三部分:抓取、建索引、检索。抓取就是通过爬虫软件自动爬取你的网站内容存储到搜索引擎的网页库中,建索引就是对抓取到的网页内容做分析并建成倒排索引,检索就是让用户在搜索框中能够搜到你的网页。

抓取的过程是通过外部指向你网站的某个链接或者你的主动推送的种子链接开始广度加深度遍历,最终抓取完你的整个网站,当然后续还会不断的重试抓取你的网页,如果发现新的链接还会继续抓取,保持时新性。

为了让搜索引擎抓取我们的网站,就需要把我们的网站结构做的足够简单清晰,建议尽量减少链接深度:爬虫是优先做广度遍历的,链接深度约小,就更容易被快速爬取

 

内链外链

爬虫是通过超链接来遍历网站的,根据排名算法,指向网站的链接越多是会提升网站排名的,所以尽量建立足够多的优质外链,这里提到优质,不是说外链越多越好,过多会被搜索引擎antispam掉,重要的是优质,有较多转化流量才能带来用户的访问,所以可以优化我们的网站,增加网页里的链接数目,比如可以在网站底部加上“最新文章”,如果你按照前面章节一步一步实现了我们的博客网站的话,那么可以修改app/Resources/views/base.html.twig,在版权说明前加上:

 

  <div class="row">
        <div class="col-sm-1 col-xs-1"></div>
        <div class="col-sm-4 col-xs-4">
            <h4 style="color: #FFFFFF; border-bottom: 1px solid #695d69; padding-bottom: 10px; margin-top: 30px;">
                最新文章</h4>
            {% for article in latestblogs %}
                <div class="row" style="margin: 10px;margin-left: 0; overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">
                    <a style="color: #959595;"
                       href="{{ path('blog_show', {'blogId':article.id}) }}">{{ article.title }}</a>
                </div>
            {% endfor %}
            <br/>
        </div>
        <div class="col-sm-1 col-xs-1"></div>
        <div class="col-sm-4 col-xs-4">
            <h4 style="color: #FFFFFF; border-bottom: 1px solid #695d69; padding-bottom: 10px; margin-top: 30px;">
                为你推荐</h4>
            {% for article in recommends %}
                <div class="row" style="margin: 10px;margin-left: 0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">
                    <a style="color: #959595;"
                       href="{{ path('blog_show', {'blogId':article.id}) }}">{{ article.title }}</a>
                </div>
            {% endfor %}
            <br/>
        </div>
        <div class="col-sm-2 col-xs-2"></div>
    </div>

并修改src/AppBundle/Controller/BlogController.php,为BlogController增加如下方法:

    public static function getLatestBlogs($contoller)
    {
        $blogPostRepository = $contoller->getDoctrine()->getRepository('AppBundle:BlogPost');
        $blogposts = $blogPostRepository->findBy(array(), array('createTime' => 'DESC'), 5);
        return $blogposts;
    }
    public static function getRecommends($contoller)
    {
        $blogPostRepository = $contoller->getDoctrine()->getRepository('AppBundle:BlogPost');
        $allrecommends = $blogPostRepository->findBy(array(), array('createTime' => 'DESC'), 100);
        $randList = BlogController::genRandList(0, sizeof($allrecommends), 5);
        $recommends = array();
        foreach ($randList as $index => $value) {
            $recommends[] = $allrecommends[$index];
        }
        return $recommends;
    }
    public static function genRandList($min, $max, $num)
    {
        $num = min($num, $max-$min);
        $map = array();
        while (sizeof($map) < $num) {
            $r = rand($min, $max-1);
            $map[$r] = 1;
        }
        return $map;
    }

请尊重原创,转载请注明来源网站www.lcsays.com以及原始链接地址

并修改listAction方法的render语句如下:

        return $this->render('blog/list.html.twig', array('pagination' => $pagination,
            'subject' => $subject,
            'latestblogs' => BlogController::getLatestBlogs($this),
            'recommends' => BlogController::getRecommends($this)));

修改showAction方法的render语句如下:

    public function showAction($blogId)
    {
        $this->blogPostRepository = $this->getDoctrine()->getRepository('AppBundle:BlogPost');
        return $this->render('blog/show.html.twig', array('blogpost' => $this->blogPostRepository->find($blogId),
            'latestblogs' => BlogController::getLatestBlogs($this),
            'recommends' => BlogController::getRecommends($this)));
    }

同时修改src/AppBundle/Controller/DefaultController.php里的indexAction方法的render语句,如下:

        return $this->render('default/index.html.twig', array(
            'subjects' => $subjects,
            'blogcounts' => $this->getSubjectBlogCountMap($subjects),
            'latestblogs' => BlogController::getLatestBlogs($this),
            'recommends' => BlogController::getRecommends($this),
        ));

看下效果:

 

解释一下,这里的getLatestBlogs(最新文章)就是获取最近发布的几篇文章,这里的getRecommends(为你推荐)其实是一个伪的推荐,里面就是做了一些随机性,这样的随机性有一些好处,就是可以让搜索引擎每次抓取的结果都有所不同,这样它会认为你的网站时刻有更新,从而缩短抓取周期,更容易发现你的新链接

 

内容质量

网站的内容质量是搜索引擎优化的关键,搜索引擎是基于文本的,所以你的页面里要尽量包含足够多的文本信息,图片和js对于搜索引擎来说无疑增加了它的分析难度。努力上传优秀的原创文章一定会受搜索引擎的青睐,一味的转载,搜索引擎是有办法知道谁是原创谁是抄袭的,原创一定会被排在前面的

 

主动提交

百度站长平台提供给了我们主动提交链接的方法,到http://zhanzhang.baidu.com/注册一个账号,获取到自动提交代码嵌入到网页里,可以在你发布文章的第一时间传给百度知晓,这块相信你自己能够搞定,我的实例代码如下,仅供参考:

<script>
(function(){
    var bp = document.createElement('script');
    bp.src = '//push.zhanzhang.baidu.com/push.js';
    var s = document.getElementsByTagName("script")[0];
    s.parentNode.insertBefore(bp, s);
})();
</script>

 

教你成为全栈工程师(Full Stack Developer) 二十二-番外篇之网站开发不能放过的小细节

请尊重原创,转载请注明来源网站www.lcsays.com以及原始链接地址

logo超链接

网站左上角的logo作为网站的一个标记,通常情况下点击会回到主页,刚好我们这部分是通过模板继承实现的,修改app/Resources/views/base.html.twig,找到logo文字的部分,加上超链接到主页,如下:

    <div class="col-sm-11 col-xs-11"><h1><a href="{{ path('homepage') }}" style="text-decoration: none;color: white;">MyWebSite</a></h1></div>

 

版权说明

 

每一个网站底部都会有一个版权说明以及备案文字,这个是我们天朝特殊要求的,如果没有在网站底部写明备案号,你的网站发布以后会在几天之内被封掉,所以还是修改app/Resources/views/base.html.twig,在</body>前增加以下内容

 

<div class="row navbar navbar-inverse" style="margin-bottom: 0;">
    <div class="row">
        <div class="col-sm-12 col-xs-12 text-center" style="color: #959595;margin-bottom: 10px;">
            Copyright © <a href="{{ path('homepage') }}">MyWebSite.com</a> | 京ICP备*****号
        </div>
    </div>
</div>

 

效果如下:

 

自定义标题

 

点开每一个网页的标题(浏览器tab页上的文字标题)应该是不同的,模板里面已经有了

<title>{% block title %}自定义标题{% endblock title %}</title>

来为我们扩展用

 

改变首页的标题,修改app/Resources/views/default/index.html.twig,在{% extends "base.html.twig" %}下增加:

{% block title %}MyWebSite - 我的网站{% endblock title %}

同样分别在app/Resources/views/blog/list.html.twig和app/Resources/views/blog/show.html.twig中也增加如下两条:

{% block title %}{{ subject.name }} - MyWebSite - 我的网站{% endblock title %}

{% block title %}{{ blogpost.title }} - lcsays - 关注大数据技术{% endblock title %}

请尊重原创,转载请注明来源网站www.lcsays.com以及原始链接地址

因为在BlogController的listAction中没有传递subject变量,所以还需要增加这个变量的传递,修改src/AppBundle/Controller/BlogController.php的listAction方法,增加:

        $this->subjectRepository = $this->getDoctrine()->getRepository('AppBundle:Subject');
        $subject = $this->subjectRepository->find($subjectId);

来获取$subject,然后把render语句改成:

        return $this->render('blog/list.html.twig', array('pagination' => $pagination,'subject' => $subject));

 

管理后台本地化

 

管理后台的一些显示文字不是很友好,比如按钮上的文字都是btn_create_and_****,这就需要我们做本地化的处理,本地化的意思就是本土化,也就是编程我们国家的语言,但是因为symfony2对中文支持不是很好,我们直接开启英文配置,修改app/config/config.yml,把

   #translator:      { fallbacks: ["%locale%"] }

前面的注释符号"#"去掉即可,按钮会变成:Create、Create and return list等

 

增加文章数目展示

首页里的类别展示中如果能展示出这一类里已经有了多少篇文章,会让用户体验更好,修改app/Resources/views/default/index.html.twig,修改显示类别名称一行为:

                <h3>{{ subject.name }}({{ blogcounts[subject.id] }})</h3>

这里的blogcounts在DefaultController.php的indexAction中没有传递,因此修改src/AppBundle/Controller/DefaultController.php,为DefaultController类增加如下方法:

    public function getSubjectBlogCountMap($subjects)
    {
        $this->blogPostRepository = $this->getDoctrine()->getRepository('AppBundle:BlogPost');
        $map = array();
        for ($i = 0; $i < sizeof($subjects); $i = $i + 1)
        {
            $this->subject = $subjects[$i];
            $map[$this->subject->getId()] = sizeof($this->blogPostRepository->findBy(array('subject' => $this->subject->getId())));
        }
        return $map;
    }

并把indexAction方法的render语句改成:

        return $this->render('default/index.html.twig', array(
            'subjects' => $subjects,
            'blogcounts' => $this->getSubjectBlogCountMap($subjects),
        ));

 

教你成为全栈工程师(Full Stack Developer) 二十一-网站开发资源汇总

请尊重原创,转载请注明来源网站www.lcsays.com以及原始链接地址

学习资源

入门教程

  • 教你成为全栈工程师(Full Stack Developer):https://www.lcsays.com/bloglist/2
    • 适合于初学者入门学习,内容涉及到前后端以及周边技术的方方面面,每部分内容虽讲的不深,但知识涉及范围比较广,看了之后可以知道自己要继续学些什么
  • HTML 30分钟入门教程:http://www.jb51.net/shouce/html/html.htm
    • html教程网上有很多,随便找一个看看就行了,深入的知识不必一次性学会,后面先用现查就行了
  • php菜鸟教程:http://www.runoob.com/php/php-tutorial.html
    • php语言上手比较简单,但是真正学精还是比较有难度的,这个菜鸟教程由浅入深教会你php
  • symfony2中文教程:http://symfony.newlifeclan.com/
    • symfony2这种框架的教程基本上就是更多讲操作步骤,很少讲原理,所以如果想快速学习,就看这个中文教程

系统教程

视频教程

请尊重原创,转载请注明来源网站www.lcsays.com以及原始链接地址

素材资源

模板素材

  • 优站精选:http://expo.bootcss.com/
    • 这里是使用了bootstrap的优秀网站,有bootstrap做基础,界面设计还是相当赞的
  • 优秀博客网站:https://www.lcsays.com
    • 这个网站是一个典型的博客网站,界面设计简约,个人比较喜欢

图片素材

社区资源

教你成为全栈工程师(Full Stack Developer) 二十-管理后台的权限控制

请尊重原创,转载请注明来源网站www.lcsays.com以及原始链接地址

SonataUserBundle

 

SonataUserBundle是sonata项目中有关用户管理的部分,它其实是集成了FOS/UserBundle组件(感兴趣可以去git上找,但个人觉得直接用SonataUserBundle就够了)并增添了一些功能,使用SonataUserBundle需要安装如下扩展,执行:

[root@centos7vm mywebsite]# composer require sonata-project/user-bundle

并修改app/AppKernel.php,增加如下组件的注册:

            new FOS\UserBundle\FOSUserBundle(),
            new Sonata\UserBundle\SonataUserBundle('FOSUserBundle'),

 

修改配置

 

修改app/config/config.yml,增加如下配置:

fos_user:
    db_driver:      orm
    firewall_name:  main
    user_class:     Sonata\UserBundle\Entity\BaseUser
    group:
        group_class:   Sonata\UserBundle\Entity\BaseGroup

 

并找到对应配置组添加如下内容:

doctrine:
    orm:
        entity_managers:
            default:
                mappings:
                    SonataUserBundle: ~

 

 

修改app/config/security.yml,改成如下样子:

security:
    role_hierarchy:
        ROLE_ADMIN:       [ROLE_USER, ROLE_SONATA_ADMIN]
        ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
        SONATA:
            - ROLE_SONATA_PAGE_ADMIN_PAGE_EDIT  # if you are using acl then this line must be commented
# http://symfony.com/doc/current/book/security.html#where-do-users-come-from-user-providers
providers:
    fos_userbundle:
        id: fos_user.user_manager

firewalls:
    # disables authentication for assets and the profiler, adapt it according to your needs
    dev:
        pattern: ^/(_(profiler|wdt)|css|images|js)/
        security: false
    # -&gt; custom firewall for the admin area of the URL
    admin:
        pattern:            /admin(.*)
        context:            user
        form_login:
            provider:       fos_userbundle
            login_path:     /admin/login
            use_forward:    false
            check_path:     /admin/login_check
            failure_path:   null
        logout:
            path:           /admin/logout
        anonymous:          true
    # -&gt; end custom configuration
    main:
        pattern:             .*
        context:             user
        form_login:
            provider:       fos_userbundle
            login_path:     /login
            use_forward:    false
            check_path:     /login_check
            failure_path:   null
        logout:             true
        anonymous:          true
access_control:
    # URL of FOSUserBundle which need to be available to anonymous users
    - { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
    # Admin login page needs to be access without credential
    - { path: ^/admin/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/admin/logout$, role: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/admin/login_check$, role: IS_AUTHENTICATED_ANONYMOUSLY }
    # Secured part of the site
    # This config requires being logged for the whole site and having the admin role for the admin part.
    # Change these rules to adapt them to your needs
    - { path: ^/admin/, role: [ROLE_ADMIN, ROLE_SONATA_ADMIN] }
    - { path: ^/.*, role: IS_AUTHENTICATED_ANONYMOUSLY }
encoders:
    FOS\UserBundle\Model\UserInterface: sha512
acl:
    connection: default</code></pre>

请尊重原创,转载请注明来源网站www.lcsays.com以及原始链接地址

修改app/config/routing.yml,添加如下内容:

sonata_user_security:
    resource: "@SonataUserBundle/Resources/config/routing/sonata_security_1.xml"
sonata_user_resetting:
    resource: "@SonataUserBundle/Resources/config/routing/sonata_resetting_1.xml"
    prefix: /resetting
sonata_user_profile:
    resource: "@SonataUserBundle/Resources/config/routing/sonata_profile_1.xml"
    prefix: /profile
sonata_user_register:
    resource: "@SonataUserBundle/Resources/config/routing/sonata_registration_1.xml"
    prefix: /register
sonata_user_change_password:
    resource: "@SonataUserBundle/Resources/config/routing/sonata_change_password_1.xml"
    prefix: /profile
sonata_user:
    resource: '@SonataUserBundle/Resources/config/routing/admin_security.xml'
    prefix: /admin

 

生成自定义用户类

 

执行:

[root@centos7vm mywebsite]# php app/console sonata:easy-extends:generate SonataUserBundle -d src

可以自动在src/Application/Sonata/UserBundle/下生成有关用户的自定义类

 

注册自定义用户类,修改app/AppKernel.php,增加:

             new Application\Sonata\UserBundle\ApplicationSonataUserBundle(),

 

重新修改配置

这时重新修改app/config/config.yml,并找到对应配置组添加如下内容:

doctrine:
    orm:
        entity_managers:
            default:
                mappings:
                    FOSUserBundle: ~
                    ApplicationSonataUserBundle: ~

 

把fos_user配置组改成如下的样子:

 

fos_user:
    db_driver:      orm
    firewall_name:  main
    user_class:     Application\Sonata\UserBundle\Entity\User
    group:
        group_class:   Application\Sonata\UserBundle\Entity\Group
        group_manager: sonata.user.orm.group_manager
    profile:
        # Authentication Form
        form:
            type:               fos_user_profile
            handler:            fos_user.profile.form.handler.default
            name:               fos_user_profile_form
            validation_groups:  [Authentication] # Please note : this is not the default value
    service:
        user_manager: sonata.user.orm.user_manager

 

生效

更新数据库,执行

[root@centos7vm mywebsite]# php app/console doctrine:schema:update --force

 

创建一个管理员账户,执行:

[root@centos7vm mywebsite]# php app/console fos:user:create yourname youemail yourpasswd --super-admin

 

请cache后重新打开http://172.16.142.134/app_dev.php/admin,会看到提示登录啦,输入刚才创建的管理员用户名和密码就可以登录啦

 

 

本章节的内容是和官方文档有所不同的,经过我的尝试以及网上的一些说法也都表示sonata官方文档里的方法是有问题的,达不到想要的目的而且会报错,按照我上面试验过的方法是可行的

 

至此,你的管理后台就有权限控制了,不会被其他人篡改,可以尽情发布了

linux自动备份文件到百度云方法

请尊重原创,转载请注明来源网站www.lcsays.com以及原始链接地址

申请百度网盘

 

首先你需要拥有自己的百度网盘,如果没有的话就去http://pan.baidu.com申请一个

 

安装bypy

 

首先你的linux机器上要安装了python,并且要安装了bypy包,如果没有就通过pip安装,安装方法是:

sudo pip install requests
sudo pip install bypy

注意:如果你没有安装pip,那么要先安装pip,方法是:

yum install python-pip

执行bypy输出帮助信息说明安装成功

 

bypy账号授权

 

第一次执行bypy list会提示

Please visit:
https://openapi.baidu.com/oauth/2.0/authorize?scope=basic+netdisk&redirect_uri=oob&response_type=code&client_id=q8WE4EpCsau1oS0MplgMKNBn
And authorize this app
Paste the Authorization Code here within 10 minutes.
Press [Enter] when you are done

这句话的意思是要打开这个链接来获取授权码,输入之后方能完成授权

请尊重原创,转载请注明来源网站www.lcsays.com以及原始链接地址

获取授权码

 

打开https://openapi.baidu.com/oauth/2.0/authorize?scope=basic+netdisk&redirect_uri=oob&response_type=code&client_id=q8WE4EpCsau1oS0MplgMKNBn

网页如下:

把复制好的授权码粘贴到终端里回车后完成授权

bypy使用方法

  • bypy upload mylocalfile myremodefile可以指定本地文件或目录,以及远程文件或目录名,实现本地文件的上传,最终文件会传到百度云盘目录的”我的应用数据(apps)"下的bypy下
  • bypy list列出远程数据
  • 其他使用方法可以参考https://github.com/houtianze/bypy

自动备份脚本

分享一下我的网站自动备份的脚本

#!/bin/bash
date
DATE=`date +%Y%m%d`
TARGET=${DATE}.tar.gz
rm -rf 2016*
mkdir $DATE
mysqldump -uroot -pmypassword shareditor > ${DATE}/shareditor.sql
cp /etc/nginx/conf.d/shareditor.conf ${DATE}
cp -r /data/httpdir/shareditor/web/uploads/ ${DATE}
tar zcvf $TARGET ${DATE}
bypy upload $TARGET $TARGET
exit 0

讲解一下大体功能:创建临时目录,把导出的数据库备份、网站配置、上传的文件都拷贝到临时目录,压缩到tar.gz文件,执行bypy上传到百度云

看我的百度云上传后的效果:

教你成为全栈工程师(Full Stack Developer) 十九-文章内容展示页面设计

请尊重原创,转载请注明来源网站www.lcsays.com以及原始链接地址

修改controller

 

修改src/AppBundle/Controller/BlogController.php,把BlogController类的showAction改成:

    public function showAction($blogId)
    {
        $this->blogPostRepository = $this->getDoctrine()->getRepository('AppBundle:BlogPost');
        return $this->render('blog/show.html.twig', array('blogpost' => $this->blogPostRepository->find($blogId)));
    }

解释一下

$blogId是BlogPost的id号,用来在url中传入参数,后面会通过路由配置来对应。

render函数通过参数传入的$this->blogPostRepository->find($blogId)是通过BlogPost这个model获取id为$blogId的文章的BlogPost实例

 

修改模板

 

修改app/Resources/views/blog/show.html.twig,把内容清掉并改成:

{% extends "base.html.twig" %}
{% block body %}
<div class="container-fluid">
    <div class="row">
        <div class="col-sm-3 col-xs-1"></div>
        <div class="col-sm-6 col-xs-10">
            <div class="row">
                <h1>{{ blogpost.title }}</h1>
            </div>
            <div class="row">
                <a class="btn btn-info btn-xs" href="{{ path('blog_list', {'subjectId':blogpost.subject.id}) }}">
                    {{ blogpost.subject.name }}
                </a>
                <a class="btn btn-success btn-xs" href="{{ path('blog_list', {'subjectId':blogpost.subject.id}) }}">
                    {{ blogpost.category.name }}
                </a>
                <small>发表于 {{ blogpost.createTimeStr }}</small>
            </div>
            <div class="row">
                <hr/>
            </div>
            <div class="row">
                <div class="row">
                    <div class="col-sm-12 col-xs-12">
                        <img style="width: 100%; height: 100%;" src="/uploads/media/default/0001/01/thumb_{{ blogpost.image.id }}_default_big.{{ blogpost.image.extension }}">
                    </div>
                </div>
                <br/>
                {{ blogpost.body|raw }}
            </div>
        </div>
        <div class="col-sm-3 col-xs-1"></div>
    </div>
</div>
{% endblock %}

配置路由

请尊重原创,转载请注明来源网站www.lcsays.com以及原始链接地址

修改app/config/routing.yml,把之前写过的:

blog_show:
    path:     /blogshow/
    defaults: { _controller: AppBundle:Blog:show }

改成:

blog_show:
    path:     /blogshow/{blogId}
    defaults: { _controller: AppBundle:Blog:show }

 

打开博客列表页,点击一篇文章标题,看下效果吧

总结

至此,我们的网站内容已经达到可以发布上线的状态了,方法可以参考前面章节讲过的服务器那些事,你可以申请一个阿里云服务器,注册个域名,部署上去,就像我的网站www.lcsays.com一样,以后只需要在后台管理管理文章就行了

机器学习教程 二-安装octave绘制3D函数图像

 

请尊重原创,转载请注明来源网站www.lcsays.com以及原始链接地址

安装方式

mac系统

安装教程在:

http://wiki.octave.org/Octave_for_MacOS_X#Binary_installer_for_OSX_10.9.1

https://www.macports.org/install.php下载对应osx版本的安装包,下载安装

port会把下载的包安装到/opt/local/var/macports/

如果没装xcode命令行工具,需要安装:

xcode-select --install

然后执行

sudo port install atlas +gcc5
sudo port install arpack -accelerate+atlas
sudo port install texlive-bin
sudo port install texlive-basic
sudo port install texlive-latex
sudo port install octave +qtgui+gui

centos7系统

首先必须保证以图形界面启动,如果没有安装图形界面则先安装:

sudo  yum groupinstall "GNOME Desktop" "Graphical Administration Tools"
sudo ln -sf /lib/systemd/system/runlevel5.target /etc/systemd/system/default.target

重启后就会以图形界面形式启动

请尊重原创,转载请注明来源网站www.lcsays.com以及原始链接地址

然后安装octave

yum install epel-release
yum install octave

 

使用样例

命令行执行octave打开octave终端,输入如下语句:

n = 50;
x = y = linspace (-8, 8, n)';
[xx, yy] = meshgrid (x, y);
r = sqrt (xx .^ 2 + yy .^ 2) + eps;
c = 5 * sin (r) ./ r;
h= surf(xx,yy,c,c);
shading interp

会自动弹出图像效果如下:

是不是很漂亮的说!

解释一下

n = 50;
x = y = linspace (-8, 8, n)';

这两句是说把x和y都赋值成从-8到8之间划分成50等份的一个个点

[xx, yy] = meshgrid (x, y);

这句是说把[xx,yy]赋值成由x和y构成的网格点

r = sqrt (xx .^ 2 + yy .^ 2) + eps;
c = 5 * sin (r) ./ r;

这两句实现了一个公式来计算c值

h= surf(xx,yy,c,c);

这句就是在画图像,surface表示把图像表面展示出来

shading interp

这句就是做了一个图像的平滑处理

北京市妇产医院人气排名

排名 医院名 人气
1 北京妇产医院 40284
2 海淀妇幼保健院 27872
3 通州区妇幼保健院 17341
4 北京协和医院 15191
5 北京大学第一医院(北大妇儿) 13585
6 北医三院(北京大学第三医院) 12471
7 朝阳妇幼保健院(朝阳区妇儿医院) 12467
8 北京顺义区妇幼保健院 10150
9 航空总医院 7683
10 大兴人民医院 7209
11 北京友谊医院 7108
12 北京房山区妇幼保健院 6819
13 海军总医院 6780
14 宣武医院 6752
15 昌平区妇幼保健院 6184
16 北京市仁和医院 5967
17 北京医院 5851
18 北京美中宜和妇儿医院 5761
19 北大人民医院 5752
20 北京复兴医院 5507
21 安贞医院 5178
22 武警总医院 4688
23 民航总医院 4617
24 潞河医院 4482
25 北京军区总医院 4414
26 大兴区妇幼保健院 4404
27 北京朝阳医院 4213
28 世纪坛医院 4165
29 积水潭医院 4052
30 306医院 3981
31 北京地坛医院 3921
32 丰台医院 3736
33 309医院 3559
34 平谷区医院 3554
35 清华大学玉泉医院 3515
36 亦庄同仁医院 3441
37 上地医院 3394
38 中日友好医院 3374
39 北京天坛医院 3344
40 航天总医院 3281
41 怀柔第一医院 3271
42 丰台妇幼保健医院 3099
43 北京市昌平区中西医结合医院 3093
44 中国人民解放军总医院(301医院) 3084
45 北京昌平区医院 3008
46 空军总医院 2926
47 和美妇儿医院 2768
48 北京东城区第一妇幼保健院(北京东四妇产医院) 2709
49 北京同仁医院 2625
50 解放军总医院第一附属医院(304医院) 2553
51 北京和睦家医院 2516
52 朝阳医院京西院区 2515
53 北京市房山区良乡医院 2499
54 北京顺义医院 2338
55 北京佑安医院 2337
56 北京密云县妇幼保健院 2328
57 华信医院 2203
58 二炮总医院 2134
59 电力医院 2129
60 航天中心医院 1996
61 煤炭总医院 1952
62 海淀医院 1934
63 延庆县医院 1912
64 密云县医院 1889
65 北京市门头沟区区医院 1845
66 石景山医院 1806
67 垂杨柳医院 1702
68 北京美中宜和妇儿医院 1590
69 首钢医院 1555
70 北京燕化凤凰医院 1459
71 平谷区妇幼保健院 1299
72 北京新世纪妇儿医院 1239
73 五洲女子医院 1185
74 顺义区中医医院 1168
75 京煤集团总医院 1162
76 四季青医院 1155
77 昌平中医院 1127
78 北京宝岛妇产医院 1115
79 北京市房山区第一医院 988
80 中国人民解放军第302医院 946
81 466医院 933
82 健宫医院 926
83 西城区妇幼保健院 890
84 北京玛丽妇婴医院 839
85 怀柔妇幼保健院 780
86 北京第六医院 775
87 北京中西医结合医院 747
88 铁营医院 741
89 北京市大兴区红星医院 728
90 长辛店医院 715
91 731医院 706
92 263医院 650
93 北京俪婴妇产医院 631
94 南苑医院 608
95 门头沟妇幼保健院 604
96 昌平妇幼保健院 589
97 朝阳区第二医院 583
98 房山中医院 577
99 燕郊京东中美医院 575
100 北京武警总队医院 501
101 北京市回民医院 499
102 延庆县妇幼保健院 497
103 北京回民医院 425
104 北京安太医院 403
105 通州区中医院 336
106 中国人民解放军316医院 324
107 北京美华妇儿医院 316
108 中国人民解放军第261医院 308
109 清华大学第一附院 295
110 北京四季青医院 255
111 北京华府妇儿医院 246
112 丰台区妇幼保健院 223
113 北京隆福医院 197
114 展览路医院 186
115 北京万柳美中宜和妇儿医院 167
116 回龙观医院 161
117 北京明德医院 154
118 天通苑医院 147
119 首钢矿山医院 62
120 北京首儿李桥儿童医院 60
121 北京中医医院 60
122 东直门医院 58
123 都市丽人医院 54
124 和平里医院 54
125 沙河医院 50
126 北京东方医院 49
127 北京普仁医院 48
128 望京医院 45
129 北京市西城区展览路医院 43
130 广安门中医院 43
131 北京天伦医院 43
132 北京鼓楼医院 37
133 大兴中医院 37
134 北京博爱医院 32
135 北京天使妇儿医院 31
136 北京中医药大学第三附属医院 28
137 西苑医院 22
138 宣武中医院 21
139 朝阳区第三医院 20
140 北京市第二医院 19
141 北京小庄医院 17
142 延庆县中医院 13
143 阜外医院 13
144 中关村医院 11
145 护国寺中医院 7
146 北京安定医院 7
147 北医六院 7
148 北京二龙路医院 6
149 305医院 6

教你成为全栈工程师(Full Stack Developer) 十八-让网站分类列表页变得更漂亮

请尊重原创,转载请注明来源网站www.lcsays.com以及原始链接地址

添加图片属性

 

有图有真相!一篇好的博客怎么能没有漂亮的图片来搭配呢?我们来在BlogPost中增加图片属性,之后展示到列表页和博客内容页中。

修改src/AppBundle/Entity/BlogPost.php,增加如下成员变量:

    /**
     * @var Media
     *
     * @ORM\ManyToOne(targetEntity="Application\Sonata\MediaBundle\Entity\Media")
     */
    private $image;

 

同时增加如下get和set方法:

    /**
     * @return Media
     */
    public function getImage()
    {
        return $this->image;
    }
    /**
     * @param Media $image
     */
    public function setImage($image)
    {
        $this->image = $image;
    }

 

执行

[root@centos7vm mywebsite]# php app/console doctrine:schema:update --force

更新数据库

 

这里你不必担心已经写入的数据库被冲掉,它其实调用的是mysql的alter语句,加一列不会删数据

 

添加图片属性的管理项

 

修改src/AppBundle/Admin/BlogPostAdmin.php中的configureFormFields方法,在

            ->add('title', 'text')

下面加入一句:

            ->add('image', 'sonata_type_model', array(
                'property' => 'name'
            ))

并修改configureListFields方法,在

            ->add('category.name')

后面加入一句:

            ->add('image.name')

 

OK,重新进入管理后台,编辑一个Blog看到多处了这样的编辑项:

 

这里图片可以选择,也可以添加,我们把已有的每一篇文章都添加一张不同的图片,最终像下面的样子:

 

 

文章摘要生成

 

每篇博客可能会很长,但是希望它能在列表页中展示一个摘要文本,通过文章生成摘要是一个long story啊,太难了,所以我们干脆在每篇文章撰写时先写一段摘要放到篇头,然后用一个分割线表明下面才是真正内容。好,我们修改src/AppBundle/Entity/BlogPost.php文件里的BlogPost类,添加一个getAbstract方法,如下:

    /**
     * body去标签然后截取前多少个字,在twig中使用方法:{{ article.abstract }}
     * @return string
     */
    public function getAbstract()
    {
        $pos = strpos($this->body, '<div style="page-break-after');
        if ($pos > 0) {
            return strip_tags(substr($this->body, 0, $pos));
        } else {
            return strip_tags(mb_substr($this->body, 0, 100, "utf-8")) . '...';
        }
    }

讲解一下,这里的'<div style="page-break-after'是经过实践得出的一个分割线的标识,也就是我们在博客编辑框(ckeditor)里面输入这个东西的效果:

 

strip_tags是php自带的函数,用来去掉html标签,保留文本内容。

请尊重原创,转载请注明来源网站www.lcsays.com以及原始链接地址

总结起来这个getAbstract的功能就是把BlogPost的body内容在分割线前面的部分提出来,并提出其中的纯文本内容,如果body中没有找到分割线,那么就截取前100个字符。

 

 

 

文章创建时间规范化

 

我们数据库中文章创建时间是DateTime类型,也就是年月日时分秒,我们在页面展示时并不需要这么详细,所以对这个字段做一些简化处理,同样在BlogPost类中添加如下方法:

    /**
     * 创建时间转成字符串才能展示
     * @return string
     */
    public function getCreateTimeStr()
    {
        $newDate = $this->createTime->format('Y-m-d H:i');
        return $newDate;
    }

 

修改模板文件

 

重新修改app/Resources/views/blog/list.html.twig,内容改成如下:

 

{% extends "base.html.twig" %}
{% block body %}
<div class="row">
    <div class="col-sm-3 col-xs-1"></div>
    <div class="col-sm-6 col-xs-10">
        <br />
        {% for article in pagination %}
            <div style="background-color: whitesmoke; padding-left: 30px;padding-right: 30px;padding-top: 10px;">
                <div class="row">
                    <div class="col-sm-3 col-xs-10">
                        <a href="{{ path('blog_show', {'blogId':article.id}) }}">
                            <img style="width: 100%; height: 100%; margin:10%;" src="/uploads/media/default/0001/01/thumb_{{ article.image.id }}_default_small.{{ article.image.extension }}">
                        </a>
                    </div>
                    <div class="col-sm-1 col-xs-2"></div>
                    <div class="col-sm-7 col-xs-12">
                        <div class="row">
                            <h3><a href="{{ path('blog_show', {'blogId':article.id}) }}">{{ article.title }}</a></h3>
                        </div>
                        <div class="row">
                            <a class="btn btn-info btn-xs" href="{{ path('blog_list', {'subjectId':article.subject.id}) }}">
                                {{ article.subject.name }}
                            </a>
                            <a class="btn btn-success btn-xs" href="{{ path('blog_list', {'subjectId':article.subject.id}) }}">
                                {{ article.category.name }}
                            </a>
                            <small>发表于 {{ article.createTimeStr }}</small>
                        </div>
                        <br />
                        <div class="row">
                            <small>{{ article.abstract }}</small>
                        </div>
                    </div>
                    <div class="col-sm-1"></div>
                </div>
                <br />
            </div>
            <br />
        {% endfor %}
        <div class="navigation">
            {{ knp_pagination_render(pagination) }}
        </div>
    </div>
    <div class="col-sm-3 col-xs-1"></div>
</div>
{% endblock body %}

 

编辑我们的文章,都添加上分割线,重新打开我们的网站,进到博客列表页,看下效果还不错

教你成为全栈工程师(Full Stack Developer) 十七-网站分类列表页面设计

请尊重原创,转载请注明来源网站www.lcsays.com以及原始链接地址

创建一些文章

 

首先,为了能展示我们的效果,我们先在后台创建几篇文章,例如如下:

 

 

 

安装分页插件

 

列表性质的页面一般都需要做分页处理,这个工作要是让我们自己处理是十分繁琐的,所以我们利用symfony2的扩展knp-paginator-bundle。

 

修改app/AppKernel.php文件,增加如下注册语句:

new Knp\Bundle\PaginatorBundle\KnpPaginatorBundle(),

 

修改app/config/config.yml,增加如下配置:

knp_paginator:
    page_range: 5                      # default page range used in pagination control
    default_options:
        page_name: page                # page query parameter name
        sort_field_name: sort          # sort field query parameter name
        sort_direction_name: direction # sort direction query parameter name
        distinct: true                 # ensure distinct results, useful when ORM queries are using GROUP BY statements
    template:
        pagination: KnpPaginatorBundle:Pagination:twitter_bootstrap_v3_pagination.html.twig     # sliding pagination controls template
        sortable: KnpPaginatorBundle:Pagination:sortable_link.html.twig # sort link template

 

解释一下,KnpPaginator是一个分页插件,配置项中page_range是默认的每一页的条目数,其他几个配置项可以不用详细了解

pagination: KnpPaginatorBundle:Pagination:twitter_bootstrap_v3_pagination.html.twig这个是配置分页中底部页码部分的样式模板,有几种可以选择:

  • KnpPaginatorBundle:Pagination:sliding.html.twig (by default)
  • KnpPaginatorBundle:Pagination:twitter_bootstrap_v3_pagination.html.twig
  • KnpPaginatorBundle:Pagination:twitter_bootstrap_pagination.html.twig
  • KnpPaginatorBundle:Pagination:foundation_v5_pagination.html.twig

 

 

修改controller

 

修改src/AppBundle/Controller/BlogController.php,把其中的listAction函数内容改成:

    public function listAction(Request $request, $subjectId)
    {
        $em    = $this->get('doctrine.orm.entity_manager');
        $dql   = "SELECT a FROM AppBundle:BlogPost a WHERE a.subject=" . $subjectId . " ORDER BY a.createTime DESC";
        $query = $em->createQuery($dql);
        $paginator  = $this->get('knp_paginator');
        $pagination = $paginator->paginate(
            $query,
            $request->query->get('page', 1)/*page number*/,
            10/*limit per page*/
        );
        return $this->render('blog/list.html.twig', array('pagination' => $pagination));
    }

 

解释一下,注意这次listAction参数里除了$request之外,多出了一个$subjectId,这是和后面要配置的路由相对应的,表示会在url里传过来一个subjectId的数值。

看$em这一行,在symfony2中对model的操作都需要先获取到EntityManager进行操作

第二行$dql赋值是一条sql语句,这里面根据$subjectId来从数据库里取BlogPost,并且按照createTime来排序,这里面需要注意的是:sql语句中写的变量名不是数据库表的字段名(小写),而是model类里定义的成员名

$pagination这一行则是调用分页插件的函数,默认取第一页,最后一个参数10表示每页展示多少条,这个会冲掉app/config/config.yml配置的5,这里面的$pagination是一个对象数组,每个成员都是一个BlogPost这个model的对象

 

 

修改模板

请尊重原创,转载请注明来源网站www.lcsays.com以及原始链接地址

修改app/Resources/views/blog/list.html.twig,把内容改成:

{% extends "base.html.twig" %}
{% block body %}
<div class="row">
    <div class="col-sm-3 col-xs-1"></div>
    <div class="col-sm-6 col-xs-10">
        <br />
        {% for article in pagination %}
        <div style="background-color: whitesmoke; padding-left: 30px;padding-right: 30px;padding-top: 10px;">
            <div class="row">
                <div class="col-sm-2 col-xs-2"></div>
                <div class="col-sm-8 col-xs-12">
                    <div class="row">
                        <h3>{{ article.title }}</h3>
                    </div>
                </div>
                <div class="col-sm-2"></div>
            </div>
            <br />
        </div>
        <br />
        {% endfor %}
        <div class="navigation">
            {{ knp_pagination_render(pagination) }}
        </div>
    </div>
    <div class="col-sm-3 col-xs-1"></div>
</div>
{% endblock body %}

 

解释一下,pagination是一个对象数组,for循环遍历每一个对象,通过{{ article.title }}获取它的title属性展示出来

下面的{{ knp_pagination_render(pagination) }}是分页插件特有的功能,就是底部页码的按钮部分

 

配置路由

 

修改app/config/routing.yml,把曾经添加过的:

blog_list:
    path:     /bloglist/
    defaults: { _controller: AppBundle:Blog:list }

 

改成:

blog_list:
    path:     /bloglist/{subjectId}
    defaults: { _controller: AppBundle:Blog:list }

 

解释一下,这里的{subjectId}会自动传到controller中,并以参数形式传到listAction里,见上面listAction函数的实现

 

好,现在打开http://172.16.142.130/app_dev.php/bloglist/4,(这里的4是Subject的id,你可以查看你的数据库看你的subject的id都有哪些值)

 

 

从首页点进来

 

我们先打开首页http://172.16.142.130/app_dev.php,点击一个图标,就能直接进到博客列表页,这是怎么做到的呢?其实我们在上一节已经做好了相关准备,看下app/Resources/views/default/index.html.twig里的这几句:

        <a href="{{ path('blog_list', {'subjectId':subject.id}) }}" class="thumbnail">
            <img src="{{ subject.photo }}" alt="{{ subject.name }}">
            <div class="caption">
                <h3>{{ subject.name }}</h3>
                <p>{{ subject.introduce }}</p>
            </div>
        </a>

 

这里面的a标签已经把跳转链接写成了{{ path('blog_list', {'subjectId':subject.id}) }},它会自动从路由配置里找到名字为blog_list的路由,并把subject.id的值传递到subjectId变量中

 

下一节我们继续对博客列表也做一些美化,然后开发博客的展示页