路由
# 主路由配置app/config/routing.yml
# 加载某模块的annotation路由,如AcmeBundle
acme:
resource: @XxxxBundle/Controller #该级目录及子目录annotation都被解析
type: annotation #必选
prefix: '' #路由前缀
# annotation配置路由
# 注解命令中引号必须用双引号
# 注解命令必须以 /** 打头, 注释以 /* 打头。
/**
* @Route(
"/Path/{slug}",
defaults: {},
name="",
requirements: {"_format": "html|rss"}
);
* @Method("GET");
*/
public function indexAction($slug){}
# annotation路由参数转换 功能
# typehint参数为一个doctrine实体类,自动查询匹配路由参数的实体对象
# 没有匹配对象则返回404页
/**
* @Route("/{id}", name="admin_post_show")
*/
public function showAction(Post $post){}
# 还可明确的设定参数转换细节
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
* @Route("/comment/{postSlug}/new", name = "comment_new")
* @ParamConverter("post", options={"mapping": {"postSlug": "slug"}})
app/console debug:router 【routeName】//调试路由
url、 参数相互转换:
$params = $this->get('router')->match('/blog/my-blog-post'); //assoc-array, 匹配的路由参数
$uri = $this->get('router')->generate('blog_show', array('slug' => 'my-blog-post'), bool $生成绝对地址); //生成url
控制器
return $this->redirectToRoute('hello', array('name' => 'Fabien')); //跳转
return $this->redirect($this->generateUrl('homepage'), 301); //重定向
return $this->forward('AcmeHelloBundle:Hello:fancy', array $context); //内部action转发
模型AR查询:
//管理器插入实体
$em = $this->getDoctrine()->getManager();
$em->persist($product);
$em->flush();
//仓库中获取实体
$repository = $this->getDoctrine()->getRepository('AppBundle:Product');
$repository->find($id);
$repository->findOneByName('foo');
$repository->findBy(array $filters);
$repository->findOneBy(array $filters);
$repository->findByPrice(19.99);
$repository->findAll();
//更新实体
$em = $this->getDoctrine()->getManager();
$product = $em->getRepository('AppBundle:Product')->find($id);
$product->setName('New product name!');
$em->flush();
//删除实体
$em->remove($product);
$em->flush();
//调用repository类方法
$repo = $this->getDoctrine()->getRepository();
$result = $repo->仓库定义的方法();
模型DQL查询
1DQL查询构建器:
$query = $repository->createQueryBuilder('p')
->where('p.price > :price')
->setParameter('price', '19.99')
->orderBy('p.price', 'ASC')
->getQuery();
$products = $query->getResult();
$product = $query->getSingleResult(); //没有结果会抛出异常
$product = $query->getOneOrNullResult(); //没有结果不会抛出异常
2纯DQL查询:
$em = $this->getDoctrine()->getManager();
$query = $em->createQuery(
'SELECT p
FROM AppBundle:Product p
WHERE p.price > :price
ORDER BY p.price ASC'
)->setParameter('price', '19.99');
$products = $query->getResult();
Doctrine
连接配置: app/config/parameters.yml
doctrine配置: app/config/config.yml
app/console doctrine:database:create //创建库(必须先创建好具备建库权限的账号)
app/console doctrine:database:drop --force //卸载库
app/console doctrine:generate:entity 【--entity="AppBundle:Category"】 //创建实体
app/console doctrine:schema:update --force //更新映射,【可创建表】
app/console doctrine:generate:entities 【AppBundle】 //为bundle生成getter,setter, repo等
一个bundle只可以接受一种metadata定义格式(来指定字段类型),表名是可选的,如果省略,将基于entity类的名称自动确定。
受保护的SQL字段(如group和user)
基础查询方法:
$repo->findOneBy(Array)
->findLatest()
关联查询:
查询到的关联对象都是延迟加载
查询到的关联对象都是代理对象(代理类存储在cache目录), 除非调用getter方法才会实际进行查询
join一次性全查关联对象:
$product = $this->getEntityManager()
->createQuery(
'SELECT p, c FROM AppBundle:Product p
JOIN p.category c
WHERE p.id = :id'
)->setParameter('id', $id)
->getSingleResult();
$category = $product->getCategory();
生命周期回调lifecycle callback:
* @ORM\HasLifecycleCallbacks() //实体头部声明
* @ORM\PrePersist //实体内部注册回调方法并在头部声明类似该执行点
TWIG
控制器访问模板路径格式:
AcmeDemoBundle:Default:index.html.twig #模板保存在src/Bundle/Resource/views
default/index.html.twig #模板保存在app/Resource/views, 最佳实践推荐模板保存在此处
定界符后需要跟一对空格
app/console twig:lint app/Resources/views【/article/recent_list.html.twig】 //模板语法检查
{{ article.body|raw }} //关闭输出转义
Hello <?php echo $view->escape($name【, 'js'】) ?> //php模板形式的转义, 默认html上下文, 可以指定js上下文
全局模板变量:
app.user //当前用户对象
app.request //当前Request对象
app.session //Session对象
app.environment //当前应用程序的环境(dev,prod等)
app.debug //如果是true说明是调试模式,false则不是。
<a href="{{ path('blog_show', {'slug': 'my-blog-post'}) }}"> //相对地址url, 可通过_format指定请求资源格式
<a href="{{ url('blog_show', {'slug': 'my-blog-post'}) }}"> //绝对地址url
{{ parent() }} //应用父模块内容
{{ include('article/article_details.html.twig', { 'article': article }) }} //包含模板块
{{ render(controller('AcmeArticleBundle:Article:recentArticles', { 'max': 3 })) }} //嵌入控制器
异步内容hinclude.js:
{{ render_hinclude(controller('...'), 【{'default': 'default/content.html.twig'}】) }}
{{ render_hinclude(url('...'), 【{'default': 'Loading...'})】) }}
资源链接:
<img src="{{ asset('images/logo.png'【, version='3.0'】【, absolute=true】) }}" alt="Symfony!" /> //version: null, 或未设置则默认版本号, false则停用版本号
## 给twig拓展自己的过滤器my_filter ##
# 自定义一个拓展类,并在其中输出my_filter
# 拓展类文件推荐放在xxBundle/Tiwg下
namespace AppBundle\Twig;
class MyExtension extends \Twig_Extension{
public function getName()
{
return 'my_extension';
}
public function getFilters()
{
return array(
new \Twig_SimpleFilter(
'my_filter',
array($this, 'some_operation'),
array('is_safe' => array('html'))
),
);
}
public some_operation($content){...}
}
# 编辑app/config/services.yml
# 注册拓展类为服务,并tag其为twig.extension
services:
xx.twig.my_extension:
class: xxBundle\Twig\MyExtension
public: false
tags:
- { name: twig.extension }
{{ content|my_filter }} #过滤器使用
静态文件管理器Assetic
不使用assetic: <script src="{{ asset('js/script.js') }}"></script>
将bundle的静态文件默认拷贝安装到web目录下, 一般用于第三方bundle(没有用assetic的)的静态资源安装
app/console assets:install
//////////////////////////////
使用assetic:
压缩、合并静态文件(dev环境,每个文件依然独立;prod或者调试关闭时,文件合并)
LESS, SASS等等的编译
图片文件优化
随意目录存储静态文件
#引入一条合并的js, 来源于两个目录
{% javascripts '@AppBundle/Resources/public/js/*' '@AcmeBarBundle/Resources/public/js/form.js' %}
<script src="{{ asset_url }}"></script>
{% endjavascripts %}
#引入一条合并的css,来源于一个目录
#css的assetic路径写法特殊,@AppBundle会报错,一个已知的框架错误
#cssrewrite过滤器用于修复文件dump后, 图片相对路径出错
{% stylesheets 'bundles/app/css/*' filter='cssrewrite' %}
<link rel="stylesheet" href="{{ asset_url }}" />
{% endstylesheets %}
#引入图片
{% image '@AppBundle/Resources/public/images/example.jpg' %}
<img src="{{ asset_url }}" alt="Example" />
{% endimage %}
配置文件预定义命名资产
# app/config/config.yml
assetic:
assets:
xxxx:
inputs:
- '@AppBundle/Resources/public/js/thirdparty/jquery.js'
模板中应用命名资产
{% javascripts '@xxxx' %}
<script src="{{ asset_url }}"></script>
{% endjavascripts %}
静态资源dump合并
prod环境:
资源路径被合并,每次文件更新时,需要手动重建一下新的合并文件
app/console assetic:dump --env=prod --no-debug
dev环境:
资源路径被合并,但没有生成新文件, 内部控制器动态读取文件并合并后直接输出(文件更新立即起效,但是很慢)
# 如果静态文件加载过慢,可关闭动态生成:
# app/config/config_dev.yml #这里是!!开发环境!!下的配置文件
assetic:
use_controller: false
然后,手动dump出合并文件(每次文件更新都得dump一下)
app/console assetic:dump
app/console assetic:watch #结合参数主动监测文件是否变动然后决定更新
指定dump文件的输出路径
{% javascripts '@AppBundle/Resources/public/js/*' output='js/compiled/main.js' %}
<script src="{{ asset_url }}"></script>
{% endjavascripts %}
静态文件压缩(UglifyJS和UglifyCSS)
全局安装压缩组件
cnpm install -g uglify-js
cnpm install -g uglifycss
配置压缩组件可用:
# app/config/config.yml
assetic:
node: /usr/bin/nodejs #可选, 手动指定node路径
filters:
uglifyjs2:
bin: /usr/local/bin/uglifyjs
运用压缩组件:
{% javascripts '@AppBundle/Resources/public/js/*' filter='uglifyjs2' %}
<script src="{{ asset_url }}"></script>
{% endjavascripts %}
配置压缩组件在debug时自动关闭, 在filter上加 "?"
{% javascripts '@AppBundle/Resources/public/js/*' filter='?uglifyjs2' %}
<script src="{{ asset_url }}"></script>
{% endjavascripts %}
清理缓存, 压缩重建dump文件
指定filter作用的文件
1. filter单独某些文件:
直接在assetic标签上标示filter
2. filter特定拓展名文件
# app/config/config.yml
assetic:
filters:
coffee:
bin: /usr/bin/coffee
apply_to: "\.coffee$" //filter拓展名coffee格式的文件
图片优化filter
1. Jpegoptim
# 安装开发包 并配置 app/config/config.yml
assetic:
filters:
jpegoptim:
bin: path/to/jpegoptim
strip_all: true //可选, 用于剔除EXIF信息
max: 70 //图片质量
使用
{% image '@AppBundle/Resources/public/images/example.jpg' filter='jpegoptim' output='/images/example.jpg' %}
<img src="{{ asset_url }}" alt="Example"/>
{% endimage %}
简短语法(twig函数)实现:
# 启用配置 app/config/config.yml
assetic:
filters:
jpegoptim:
bin: path/to/jpegoptim
twig:
functions:
jpegoptim: ~ 或指定输出目录 { output: images/*.jpg }
使用 <img src="{{ jpegoptim('@AppBundle/Resources/public/images/example.jpg') }}" alt="Example"/>