neo4j 程序连接端口_使用Neo4j向PHP应用程序添加社交网络功能
neo4j 程序连接端口
In the last part, we learned about Neo4j and how to use it with PHP. In this post, we’ll be using that knowledge to build a real Silex-powered social network application with a graph database.
在最后一部分中,我们了解了Neo4j以及如何在PHP中使用它。 在本文中,我们将利用这些知识来构建一个真正的由Silex支持的带有图形数据库的社交网络应用程序。
引导应用程序 (Bootstrapping the application)
I’ll use Silex, Twig, Bootstrap and NeoClient to build the application.
我将使用Silex , Twig , Bootstrap和NeoClient来构建应用程序。
Create a directory for the app. I named mine spsocial
.
为应用创建目录。 我把我的名字叫做spsocial
。
Add these lines to your composer.json
and run composer install
to install the dependencies :
将这些行添加到您的composer.json
并运行composer install
来安装依赖项:
{
"require": {
"silex/silex": "~1.1",
"twig/twig": ">=1.8,<2.0-dev",
"symfony/twig-bridge": "~2.3",
"neoxygen/neoclient": "~2.1"
},
"autoload": {
"psr-4": {
"Ikwattro\\SocialNetwork\\": "src"
}
}
}
You can download and install Bootstrap to the web/assets
folder of your project.
您可以将Bootstrap下载并安装到项目的web/assets
文件夹中。
You can find the bootstrap demo app here as well: https://github.com/sitepoint-editors/social-network
您也可以在这里找到引导演示应用程序: https : //github.com/sitepoint-editors/social-network
设置Silex应用程序 (Set up the Silex application)
We need to configure Silex and declare Neo4jClient so it will be available in the Silex Application. Create an index.php
file in the web/
folder of your project :
我们需要配置Silex并声明Neo4jClient,以便它将在Silex应用程序中可用。 在项目的web/
文件夹中创建一个index.php
文件:
<?php
require_once __DIR__.'/../vendor/autoload.php';
use Neoxygen\NeoClient\ClientBuilder;
$app = new Silex\Application();
$app['neo'] = $app->share(function(){
$client = ClientBuilder::create()
->addDefaultLocalConnection()
->setAutoFormatResponse(true)
->build();
return $client;
});
$app->register(new Silex\Provider\TwigServiceProvider(), array(
'twig.path' => __DIR__.'/../src/views',
));
$app->register(new Silex\Provider\MonologServiceProvider(), array(
'monolog.logfile' => __DIR__.'/../logs/social.log'
));
$app->register(new Silex\Provider\UrlGeneratorServiceProvider());
$app->get('/', 'Ikwattro\\SocialNetwork\\Controller\\WebController::home')
->bind('home');
$app->run();
Twig is configured to have its template files located in the src/views
folder. A home route pointing to /
is registered and configured to use the WebController
we will create later. The application structure should look like this :
Twig配置为使其模板文件位于src/views
文件夹中。 已注册指向/
本地路由并将其配置为使用我们稍后将创建的WebController
。 应用程序结构应如下所示:
Note that here I used bower to install bootstrap, but it is up to you what you want to use.
请注意,这里我使用Bower来安装引导程序,但是要由您决定使用什么。
The next step is to create our base layout with a content block that our child Twig templates will override with their own content. I’ll take the default bootstrap theme with a navbar on top:
下一步是使用内容块创建基本布局,我们的子级Twig模板将使用其自身的内容覆盖该内容块。 我将使用默认的引导主题,并在顶部带有导航栏:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
<title>My first Neo4j application</title>
<!-- Bootstrap core CSS -->
<link href="{{ app.request.basepath }}/assets/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
<style>
body { padding-top: 70px; }
</style>
</head>
<body>
<div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
<button type="button" id="collbut" class="navbar-toggle collapsed" data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">My first Neo4j application</a>
</div>
</div>
</div>
<div class="container-fluid">
{% block content %}
{% endblock content %}
</div>
</body>
</html>
主页(检索所有用户) (The home page (retrieving all users))
So far, we have Neo4j available in the application, our base template is created and we want to list all users on the home page.
到目前为止,我们在应用程序中提供了Neo4j,已经创建了基本模板,并且希望在主页上列出所有用户。
We can achieve this in two steps :
我们可以分两步实现:
Create our
home
controller action and retrieve users from Neo4j创建我们的
home
控制器操作并从Neo4j检索用户- Pass the list of users to the template and list them 将用户列表传递给模板并列出他们
控制器动作 (The Controller action)
<?php
namespace Ikwattro\SocialNetwork\Controller;
use Silex\Application;
use Symfony\Component\HttpFoundation\Request;
class WebController
{
public function home(Application $application, Request $request)
{
$neo = $application['neo'];
$q = 'MATCH (user:User) RETURN user';
$result = $neo->sendCypherQuery($q)->getResult();
$users = $result->get('user');
return $application['twig']->render('index.html.twig', array(
'users' => $users
));
}
}
The controller shows the process, we retrieve the neo
service and issue a Cypher query to retrieve all the users. The users collection is then passed to the index.html.twig
template.
控制器显示了该过程,我们检索了neo
服务并发出Cypher查询以检索所有用户。 然后将用户集合传递到index.html.twig
模板。
索引模板 (The index template)
{% extends "layout.html.twig" %}
{% block content %}
<ul class="list-unstyled">
{% for user in users %}
<li>{{ user.property('firstname') }} {{ user.property('lastname') }}</li>
{% endfor %}
</ul>
{% endblock %}
The template is very light, it extends our base layout and add an unsorted list with the user’s firstnames and lastnames in the content
inherited block.
该模板非常轻巧,它扩展了我们的基本布局,并在content
继承块中添加了带有用户的名字和姓氏的未排序列表。
Start the built-in php server and admire your work :
启动内置的php服务器并欣赏您的工作:
cd spsocial/web
php -S localhost:8000
open localhost:8000
社交网络功能:显示用户关注的人 (Social Network Features: Showing whom a user follows)
Let’s say now that we want to click on a user, and be presented his detail information and the users he follows.
现在假设我们要单击一个用户,并向其显示他的详细信息和他所关注的用户。
Step 1 : Create a route in index.php
步骤1:在index.php
创建一条路由
$app->get('/user/{login}', 'Ikwattro\\SocialNetwork\\Controller\\WebController::showUser')
->bind('show_user');
Step 2: Create the showUser
controller action
步骤2:创建showUser
控制器操作
public function showUser(Application $application, Request $request, $login)
{
$neo = $application['neo'];
$q = 'MATCH (user:User) WHERE user.login = {login}
OPTIONAL MATCH (user)-[:FOLLOWS]->(f)
RETURN user, collect(f) as followed';
$p = ['login' => $login];
$result = $neo->sendCypherQuery($q, $p)->getResult();
$user = $result->get('user');
$followed = $result->get('followed');
if (null === $user) {
$application->abort(404, 'The user $login was not found');
}
return $application['twig']->render('show_user.html.twig', array(
'user' => $user,
'followed' => $followed
));
}
The workflow is similar to any other applications, you try to find the user based on the login. If it does not exist you show a 404 error page, otherwise you pass the user data to the template.
该工作流程类似于任何其他应用程序,您尝试根据登录名查找用户。 如果不存在,则显示404错误页面,否则将用户数据传递给模板。
Step 3 : Create the show_user
template file
步骤3:建立show_user
范本档案
{% extends "layout.html.twig" %}
{% block content %}
<h1>User informations</h1>
<h2>{{ user.property('firstname') }} {{ user.property('lastname') }}</h2>
<h3>{{ user.property('login') }}</h3>
<hr/>
<div class="row">
<div class="col-sm-6">
<h4>User <span class="label label-info">{{ user.property('login') }}</span> follows :</h4>
<ul class="list-unstyled">
{% for follow in followed %}
<li>{{ follow.property('login') }} ( {{ follow.property('firstname') }} {{ follow.property('lastname') }} )</li>
{% endfor %}
</ul>
</div>
</div>
{% endblock %}
Step 4 : Refactor the list of users in the homepage to show links to their profile
步骤4:重构首页中的用户列表,以显示指向其个人资料的链接
{% for user in users %}
<li>
<a href="{{ path('show_user', { login: user.property('login') }) }}">
{{ user.property('firstname') }} {{ user.property('lastname') }}
</a>
</li>
{% endfor %}
Refresh the homepage and click on any user for showing his profile and the list of followed users
刷新主页,然后单击任何用户以显示其个人资料和关注的用户列表
添加建议 (Adding suggestions)
The next step is to provide suggestions to the profile. We need to slightly extend our cypher query in the controller by adding an OPTIONAL MATCH
to find suggestions based on the second degree network.
下一步是为个人资料提供建议。 我们需要通过添加OPTIONAL MATCH
以基于二级网络查找建议来稍微扩展控制器中的密码查询。
The optional prefix causes a MATCH
to return a row even if there were no matches but with the non-resolved parts set to null
(much like an outer JOIN). As we potentially get multiple paths for each friend-of-a-friend (fof), we need to distinct the results in order to avoid duplicates in our list (collect is an aggregation operation that collects values into an array):
即使没有匹配项,但未解析的部分设置为null
(类似于外部JOIN),可选的前缀也会使MATCH
返回一行。 由于我们可能为每个“朋友”(fof)获得多个路径,因此我们需要区分结果以避免在列表中出现重复(collect是将值收集到数组中的聚合操作):
The updated controller :
更新的控制器:
public function showUser(Application $application, Request $request, $login)
{
$neo = $application['neo'];
$q = 'MATCH (user:User) WHERE user.login = {login}
OPTIONAL MATCH (user)-[:FOLLOWS]->(f)
OPTIONAL MATCH (f)-[:FOLLOWS]->(fof)
WHERE user <> fof
AND NOT (user)-[:FOLLOWS]->(fof)
RETURN user, collect(f) as followed, collect(distinct fof) as suggestions';
$p = ['login' => $login];
$result = $neo->sendCypherQuery($q, $p)->getResult();
$user = $result->get('user');
$followed = $result->get('followed');
$suggestions = $result->get('suggestions');
if (null === $user) {
$application->abort(404, 'The user $login was not found');
}
return $application['twig']->render('show_user.html.twig', array(
'user' => $user,
'followed' => $followed,
'suggestions' => $suggestions
));
}
The updated template :
更新的模板:
{% extends "layout.html.twig" %}
{% block content %}
<h1>User informations</h1>
<h2>{{ user.property('firstname') }} {{ user.property('lastname') }}</h2>
<h3>{{ user.property('login') }}</h3>
<hr/>
<div class="row">
<div class="col-sm-6">
<h4>User <span class="label label-info">{{ user.property('login') }}</span> follows :</h4>
<ul class="list-unstyled">
{% for follow in followed %}
<li>{{ follow.property('login') }} ( {{ follow.property('firstname') }} {{ follow.property('lastname') }} )</li>
{% endfor %}
</ul>
</div>
<div class="col-sm-6">
<h4>Suggestions for user <span class="label label-info">{{ user.property('login') }}</span> </h4>
<ul class="list-unstyled">
{% for suggested in suggestions %}
<li>{{ suggested.property('login') }} ( {{ suggested.property('firstname') }} {{ suggested.property('lastname') }} )</li>
{% endfor %}
</ul>
</div>
</div>
{% endblock %}
You can immediately explore the suggestions in your application :
您可以立即探索应用程序中的建议:
连接到用户(添加关系) (Connecting to a user (adding relationship))
In order to connect to a suggested user, we’ll add a post form link to each suggested user containing both users as hidden fields. We’ll also create the corresponding route and controller action.
为了连接到建议的用户,我们将向每个建议的用户添加一个帖子表单链接,其中包含两个用户均为隐藏字段。 我们还将创建相应的路由和控制器操作。
Creating the route :
创建路线:
#web/index.php
$app->post('/relationship/create', 'Ikwattro\\SocialNetwork\\Controller\\WebController::createRelationship')
->bind('relationship_create');
The controller action :
控制器动作:
public function createRelationship(Application $application, Request $request)
{
$neo = $application['neo'];
$user = $request->get('user');
$toFollow = $request->get('to_follow');
$q = 'MATCH (user:User {login: {login}}), (target:User {login:{target}})
MERGE (user)-[:FOLLOWS]->(target)';
$p = ['login' => $user, 'target' => $toFollow];
$neo->sendCypherQuery($q, $p);
$redirectRoute = $application['url_generator']->generate('show_user', array('login' => $user));
return $application->redirect($redirectRoute);
}
Nothing unusual here, we MATCH
for the start user node and the target user node and then we MERGE
the corresponding FOLLOWS
relationship. We use MERGE on the relationship to avoid duplicate entries.
这里没有什么异常,我们MATCH
起始用户节点和目标用户节点,然后MERGE
对应的FOLLOWS
关系。 我们在关系上使用MERGE以避免重复输入。
The template:
模板:
<div class="col-sm-6">
<h4>Suggestions for user <span class="label label-info">{{ user.property('login') }}</span> </h4>
<ul class="list-unstyled">
{% for suggested in suggestions %}
<li>
{{ suggested.property('login') }} ( {{ suggested.property('firstname') }} {{ suggested.property('lastname') }} )
<form method="POST" action="{{ path('relationship_create') }}">
<input type="hidden" name="user" value="{{ user.property('login') }}"/>
<input type="hidden" name="to_follow" value="{{ suggested.property('login') }}"/>
<button type="submit" class="btn btn-success btn-sm">Follow</button>
</form>
<hr/>
</li>
{% endfor %}
</ul>
</div>
You can now click on the FOLLOW
button of the suggested user you want to follow :
现在,您可以点击FOLLOW
要遵循建议的用户的按钮:
Removing relationships :
删除关系:
The workflow for removing relationships is pretty much the same as for adding new relationships, create a route, a controller action and adapt the layout :
删除关系的工作流程与添加新关系,创建路线,控制器动作和调整布局的工作流程几乎相同:
The route :
路线 :
#web/index.php
$app->post('/relationship/remove', 'Ikwattro\\SocialNetwork\\Controller\\WebController::removeRelationship')
->bind('relationship_remove');
The controller action :
控制器动作:
public function removeRelationship(Application $application, Request $request)
{
$neo = $application['neo'];
$user = $request->get('login');
$toRemove = $request->get('to_remove');
$q = 'MATCH (user:User {login: {login}} ), (badfriend:User {login: {target}} )
MATCH (user)-[follows:FOLLOWS]->(badfriend)
DELETE follows';
$p = ['login' => $user, 'target' => $toRemove];
$neo->sendCypherQuery($q, $p);
$redirectRoute = $application['url_generator']->generate('show_user', array('login' => $user));
return $application->redirect($redirectRoute);
}
You can see here that I used MATCH
to find the relationship between the two users, and I added an identifier follows
to the relationship to be able to DELETE
it.
你可以在这里看到我用MATCH
找到两个用户之间的关系,我添加了一个标识follows
的关系,以便能够DELETE
它。
The template :
模板:
<h4>User <span class="label label-info">{{ user.property('login') }}</span> follows :</h4>
<ul class="list-unstyled">
{% for follow in followed %}
<li>
{{ follow.property('login') }} ( {{ follow.property('firstname') }} {{ follow.property('lastname') }} )
<form method="POST" action="{{ path('relationship_remove') }}">
<input type="hidden" name="login" value="{{ user.property('login') }}"/>
<input type="hidden" name="to_remove" value="{{ follow.property('login') }}"/>
<button type="submit" class="btn btn-sm btn-warning">Remove relationship</button>
</form>
<hr/>
</li>
{% endfor %}
</ul>
You can now click the Remove relationship button under each followed user :
现在,您可以单击每个关注用户下的“ 删除关系”按钮:
结论 (Conclusion)
Graph databases are the perfect fit for relational data, and using it with PHP and NeoClient is easy. Cypher is a convenient query language you will quickly love, because it makes possible to query your graph in a natural way.
图形数据库非常适合关系数据,并且与PHP和NeoClient一起使用非常容易。 Cypher是一种您很快就会喜欢的便捷查询语言,因为它可以自然地查询图形。
There is so much benefit from using Graph Databases for real world data, I invite you to discover more by reading the manual http://neo4j.com/docs/stable/ , having a look at use cases and examples supplied by Neo4j users and following @Neo4j on Twitter.
使用Graph数据库获取现实数据有很多好处,我邀请您阅读手册http://neo4j.com/docs/stable/ ,以了解Neo4j用户提供的 用例和示例 ,并从中找到更多信息。 在Twitter上关注@ Neo4j 。
翻译自: https://www.sitepoint.com/adding-social-network-features-php-app-neo4j/
neo4j 程序连接端口