设为首页 收藏本站
查看: 596|回复: 0

[经验分享] Integration Interface to Connect with NAV(PHP)

[复制链接]

尚未签到

发表于 2017-4-5 09:54:31 | 显示全部楼层 |阅读模式
An Existing Purpose-Built Website, Synchronizing the Information


  
If you’re lucky, you will find a website solution that is purpose built for the problem you’re solving.  A retail system is a good example.  In this scenario you just need to have a mechanism to synchronize the data, and often not in real-time.  For example, prices can be updated in a batch process nightly from NAV into a retail system, and sales information can be grabbed from that system and put into sales journals and posted in Dynamics NAV.  In this scenario you only need to custom build the synchronization component.  This component could connect to either your business web services or do regular updates from a landing area in your Dynamics NAV database (for example writing sales into the sales journal, and potentially reading current pricing or inventory from Dynamics NAV).

An Existing Custom Website, That Now Needs to Integrate Some NAV Data with Existing Data


  
Sometimes you’ve got a website with a database that already exists, and you just need to grab some of the data and merge it with some other information.  

If you need to merge the data, and the data does not need to be real-time then your integration option looks similar to the 3rd party component web site.

An Internal Website, or a Website that Needs Real-time Information


  
If it’s an internal website, or need real-time data then you can connect directly the Dynamics NAV SQL database or to the Dynamics NAV web services.

There are many variations of the above.  For example, you might want database replication to keep your NAV data available and not have web usage introduce risk or load on your company operations.  

Choosing the Integration Interface to Connect with NAV
  
There are several NAV interfaces, but you’ve really got two main options.  RTC business web services (Dynamics NAV pages or CodeUnits exposed as business web services), or direct to SQL server.  The best choice will really depend on your architecture, which is also affected by your licensing.

2009 Web Services, AJAX > RTC Web Services
  
If you’re using NAV 2009 you have the option of using the RTC web services.  These web services are really meant for SOA integration, however you could just expose the RTC web services directly over the Internet and then have a completely AJAX driven website connect to that, however the web service requires you to be authenticated against Active Directory.  This has a few implications:



  • Licensing (again).   Every user would need windows CAL and a dynamics NAV C/AL.  This option is fine for your existing Dynamics NAV users as that license fee would already have been paid, but it makes it more costly to allow access for your customers or vendors.  It’s not an option at all for anonymous logins.

  • Kerberos double (or more) hop.  Having gone through this several times it’s doable, however be aware that some IT departments will not allow web accessible computers to delegate authentication, which is a key step in configuring double-hop to work properly.  Determine now before you start whether this is a possible deployment scenario, because sometimes it’s a showstopper.
  Another issue with the AJAX  Dynamics NAV Web service option is that the web services provided do not work out of the box with the two popular AJAX frameworks JQuery or EXTJS, which would require several JavaScript wrappers.  In 2013 that should change with the ODATA option that will be available.

2009 Web Services, but with Your own Custom Decorators and Controllers on Top
  
Dynamics NAV 2009 web services are WCF based, which makes it very simple to connect to from ASP.Net based systems.  This also provides the option to have your web site exposed with it’s own custom forms based authentication, while still connecting to NAV as a single application user (check your licensing options before you do this).  This makes it easy to execute NAV business logic from within your custom website, and provide access to data.

Direct to SQL Server
  
If you’re using Dynamics NAV 5 or earlier then web-services aren’t available as an option, and you will often need to just connect to SQL server directly.

Even if you’re on 2009 or later, the downside to using the NAV web services is that it is not the most efficient communication available.  For performance-minded applications this is not often a solution as you’ll have your website data layer, making an HTTP call to the web services, passing noisy XML data back and forth, and having that XML data serialize and de-serialize both on the side of Dynamics NAV and your custom website.

Your challenges with the direct to SQL Server option are:


  • No business logic is available.
  • The table naming convention of prefixing SQL server tables “CompanyName$” will make it more difficult to develop and deploy, and can also cause problems with newer frameworks such as Ruby on Rails, or MVC.Net
  In a future blog post we’ll show how to use a new framework (MVC.Net) to connect to SQL server directly for the situations where the Dynamics NAV web services are not an option, and get around the table-naming problem.


Using Services


define('USERPWD', 'domain\user:password');
class NTLMStream
{
private $path;
private $mode;
private $options;
private $opened_path;
private $buffer;
private $pos;
/**
* Open the stream
*
* @param unknown_type $path
* @param unknown_type $mode
* @param unknown_type $options
* @param unknown_type $opened_path
* @return unknown
*/
public function stream_open($path, $mode, $options, $opened_path) {
$this->path = $path;
$this->mode = $mode;
$this->options = $options;
$this->opened_path = $opened_path;
$this->createBuffer($path);
return true;
}
/**
* Close the stream
*
*/
public function stream_close() {
curl_close($this->ch);
}
/**
* Read the stream
*
* @param int $count number of bytes to read
* @return content from pos to count
*/
public function stream_read($count) {
if(strlen($this->buffer) == 0) {
return false;
}
$read = substr($this->buffer,$this->pos, $count);
$this->pos += $count;
return $read;
}
/**
* write the stream
*
* @param int $count number of bytes to read
* @return content from pos to count
*/
public function stream_write($data) {
if(strlen($this->buffer) == 0) {
return false;
}
return true;
}
/**
*
* @return true if eof else false
*/
public function stream_eof() {
return ($this->pos > strlen($this->buffer));
}
/**
* @return int the position of the current read pointer
*/
public function stream_tell() {
return $this->pos;
}
/**
* Flush stream data
*/
public function stream_flush() {
$this->buffer = null;
$this->pos = null;
}
/**
* Stat the file, return only the size of the buffer
*
* @return array stat information
*/
public function stream_stat() {
$this->createBuffer($this->path);
$stat = array(
'size' => strlen($this->buffer),
);
return $stat;
}
/**
* Stat the url, return only the size of the buffer
*
* @return array stat information
*/
public function url_stat($path, $flags) {
$this->createBuffer($path);
$stat = array(
'size' => strlen($this->buffer),
);
return $stat;
}
/**
* Create the buffer by requesting the url through cURL
*
* @param unknown_type $path
*/
private function createBuffer($path) {
if($this->buffer) {
return;
}
$this->ch = curl_init($path);
curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($this->ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_setopt($this->ch, CURLOPT_HTTPAUTH, CURLAUTH_NTLM);
curl_setopt($this->ch, CURLOPT_USERPWD, USERPWD);
$this->buffer = curl_exec($this->ch);
$this->pos = 0;
}
}

class NTLMSoapClient extends SoapClient {
function __doRequest($request, $location, $action, $version) {
$headers = array(
'Method: POST',
'Connection: Keep-Alive',
'User-Agent: PHP-SOAP-CURL',
'Content-Type: text/xml; charset=utf-8',
'SOAPAction: "'.$action.'"',
);
$this->__last_request_headers = $headers;
$ch = curl_init($location);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POST, true );
curl_setopt($ch, CURLOPT_POSTFIELDS, $request);
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_NTLM);
curl_setopt($ch, CURLOPT_USERPWD, USERPWD);
$response = curl_exec($ch);
return $response;
}
function __getLastRequestHeaders() {
return implode("\n", $this->__last_request_headers)."\n";
}
}

// we unregister the current HTTP wrapper
stream_wrapper_unregister('http');
// we register the new HTTP wrapper
stream_wrapper_register('http', 'NTLMStream') or die("Failed to register protocol");
// Initialize Soap Client
$baseURL = 'http://localhost:7047/DynamicsNAV/WS/';
$client = new NTLMSoapClient($baseURL.'SystemService');
// Find the first Company in the Companies
$result = $client->Companies();
$companies = $result->return_value;
echo "Companies:<br>";
if (is_array($companies)) {
foreach($companies as $company) {
echo "$company<br>";
}
$cur = $companies[0];
}
else {
echo "$companies<br>";
$cur = $companies;
}

Note that is return value is an array if there are multiple companies, but a company name if there is only one. I have NO idea why this is or whether I can write the code differently to avoid this.

Now I have the company I want to use in $cur and the way I create a URL to the Customer page is by doing:

$pageURL = $baseURL.rawurlencode($cur).'/Page/Customer';
echo "<br>URL of Customer page: $pageURL<br><br>";
 and then I can create a Soap Client to the Customer Page:

// Initialize Page Soap Client
$page = new NTLMSoapClient($pageURL);
 and using this, I read customer 10000 and output the name:

$params = array('No' => '10000');
$result = $page->Read($params);
$customer = $result->Customer;
echo "Name of Customer 10000:".$customer->Name."<br><br>";
 Last, but not least – lets create a filter and read all customers in GB that has Location Code set to RED or BLUE:

$params = array('filter' => array(
array('Field' => 'Location_Code',
'Criteria' => 'RED|BLUE'),
array('Field' => 'Country_Region_Code',
'Criteria' => 'GB')
),
'setSize' => 0);
$result = $page->ReadMultiple($params);
$customers = $result->ReadMultiple_Result->Customer;
Note that Bookmark is an optional parameter and doesn’t need to be specified.
Now echo the customers and restore the http protocol – again, the return value might be an array and might not.

echo "Customers in GB served by RED or BLUE warehouse:<br>";
if (is_array($customers)) {
foreach($customers as $cust) {
echo $cust->Name."<br>";
}
}
else {
echo $customers->Name."<br>";
}
// restore the original http protocole
stream_wrapper_restore('http');

 All of the above will output this when the script is opened in a browser (on my machine running NAV 2009SP1 W1)

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-360404-1-1.html 上篇帖子: (转)PHP正则表达式使用详解 下篇帖子: 微信公众平台开发php源码
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表