0

I am coding a php application which uses the D3 observable collapsible tree. A json file is created by php which is accessed by a .js file.

The relevant line in the .js which sets the file path to the json file is;

const fileAttachments = new Map([["flare-2.json",new URL("../files/paul",import.meta.url)]]);

This is working fine.

However, I want to have multiple users on the site, each with a separate json file. Therefore the .js file will need to dynamically access a different file path, depending on the user.

The php that creates the session file path is;

$file_path = "../files/$user";
$_SESSION['file_path'] = $file_path;

I have modified the .js as follows;

 const fileAttachments = new Map([["flare-2.json",new URL("<?php session_start(); $file_path = $_SESSION['file_path']; echo $file_path;?>", import.meta.url)]]);

Unfortunately, this is not establishing the file path in the .js file.

The full .js is;

// https://observablehq.com/@d3/collapsible-tree@360
export default function define(runtime, observer) {
  const main = runtime.module();
  const fileAttachments = new Map([["flare-2.json",new URL("../files/paul",import.meta.url)]]);
  main.builtin("FileAttachment", runtime.fileAttachments(name => fileAttachments.get(name)));
  main.variable(observer()).define(["md"], function(md){return(
md`# Collapsible Tree

Click a black node to expand or collapse [the tree](/@d3/tidy-tree).`
)});
  main.variable(observer("chart")).define("chart", ["d3","data","dy","margin","width","dx","tree","diagonal"], function(d3,data,dy,margin,width,dx,tree,diagonal)
{
    
    
  const root = d3.hierarchy(data);

  root.x0 = dy / 2;
  root.y0 = 0;
  root.descendants().forEach((d, i) => {
    d.id = i;
    d._children = d.children;
    if (d.depth && d.data.name.length !== 7) d.children = null;
  });

  const svg = d3.create("svg")
      .attr("viewBox", [-margin.left, -margin.top, width, dx])
      .style("overflow", "visible")
      .style("width", "100vw")
      .style("font", "1em Arial")
      .style("fill", "#383838")
      .style("padding-left", 0)
      .style("padding-top", 0)
      .style("margin-top", "5vh")
      .style("margin-left", "5vw")
      .style("top", 280)
      .style("user-select", "none");

  const gLink = svg.append("g")
      .attr("fill", "none")
      .attr("stroke", "#555")
      .attr("stroke-opacity", 0.4)
      .attr("stroke-width", 1.2);

  const gNode = svg.append("g")
      .attr("cursor", "pointer")
      .attr("pointer-events", "all");
      
  function update(source) {
    const duration = d3.event && d3.event.altKey ? 2500 : 250;
    const nodes = root.descendants().reverse();
    const links = root.links();

    // Compute the new tree layout.
    tree(root);

    let left = root;
    let right = root;
    root.eachBefore(node => {
      if (node.x < left.x) left = node;
      if (node.x > right.x) right = node;
    });

    const height = right.x - left.x + margin.top + margin.bottom;

    const transition = svg.transition()
        .duration(duration)
        .attr("viewBox", [-margin.left, left.x - margin.top, width, height])
        .tween("resize", window.ResizeObserver ? null : () => () => svg.dispatch("toggle"));

    // Update the nodes…
    const node = gNode.selectAll("g")
      .data(nodes, d => d.id);
      

    // Enter any new nodes at the parent's previous position.
    const nodeEnter = node.enter().append("g")
        .attr("transform", d => `translate(${source.y0},${source.x0})`)
        .attr("fill-opacity", 0)
        .attr("stroke-opacity", 0)
        .on("click", (event, d) => {
          d.children = d.children ? null : d._children;
          update(d);
        });

    nodeEnter.append("circle")
        .attr("r", 6)
        .attr("fill", d => d._children ? "#79ec79" : "#B0B0B0")
        .attr("stroke-width", 10);

    nodeEnter.append("text")
        .attr("dy", "0.31em")
        .attr("x", d => d._children ? -16 : 16)
        .attr("text-anchor", d => d._children ? "end" : "start")

        .text(d => d.data.name)
      .clone(true).lower()
        .attr("stroke-linejoin", "round")
        .attr("stroke-width", 3)
        .attr("stroke", "white");
        
        
    // Transition nodes to their new position.
    const nodeUpdate = node.merge(nodeEnter).transition(transition)
        .attr("transform", d => `translate(${d.y},${d.x})`)
        .attr("fill-opacity", 1)
        .attr("stroke-opacity", 1);

    // Transition exiting nodes to the parent's new position.
    const nodeExit = node.exit().transition(transition).remove()
        .attr("transform", d => `translate(${source.y},${source.x})`)
        .attr("fill-opacity", 0)
        .attr("stroke-opacity", 0);

    // Update the links…
    const link = gLink.selectAll("path")
      .data(links, d => d.target.id);

    // Enter any new links at the parent's previous position.
    const linkEnter = link.enter().append("path")
        .attr("d", d => {
          const o = {x: source.x0, y: source.y0};
          return diagonal({source: o, target: o});
        });

    // Transition links to their new position.
    link.merge(linkEnter).transition(transition)
        .attr("d", diagonal);

    // Transition exiting nodes to the parent's new position.
    link.exit().transition(transition).remove()
        .attr("d", d => {
          const o = {x: source.x, y: source.y};
          return diagonal({source: o, target: o});
        });

    // Stash the old positions for transition.
    root.eachBefore(d => {
      d.x0 = d.x;
      d.y0 = d.y;
    });
  }

  update(root);

  return svg.node();
}
);
  main.variable(observer("diagonal")).define("diagonal", ["d3"], function(d3){return(
d3.linkHorizontal().x(d => d.y).y(d => d.x)
)});
  main.variable(observer("tree")).define("tree", ["d3","dx","dy"], function(d3,dx,dy){return(
d3.tree().nodeSize([dx, dy])
)});
  main.variable(observer("data")).define("data", ["FileAttachment"], function(FileAttachment){return(
FileAttachment("flare-2.json").json()
)});
  main.variable(observer("dx")).define("dx", function(){return(
30
)});
  main.variable(observer("dy")).define("dy", ["width"], function(width){return(
width / 4
)});
  main.variable(observer("margin")).define("margin", function(){return(
{top: 10, right: 120, bottom: 10, left: 40}
)});
  main.variable(observer("d3")).define("d3", ["require"], function(require){return(
require("d3@6")
)});
  return main;
}

3 Answers 3

1

You can try to move your session_start() at the very beginning of the file. Then, in the script, using only $file_path variable. Like this:

<?php 
session_start();
$file_path = $_SESSION['file_path'];
?>
...
const fileAttachments = new Map([["flare-2.json",new URL("<?=$file_path?>", import.meta.url)]]);
Sign up to request clarification or add additional context in comments.

2 Comments

That would seem like a good idea, but doing this corrupts the .js file.
Ok, you can't use php inside a .js file. I think the best solution for you is to create a new file php that return the $file_path variable. You can fetch it inside your .js file and then use it inside the function
1

You can include a variable with you're script tag like:

<?php
  session_start();
  $file_path = $_SESSION['file_path'];
?>

<script src="{url to folder}" file-path="<?php echo $file_path;?>"></script>

And inside the js file put the following on top:

const filePath = document.currentScript.getAttribute('file-path');

Now you have a variable with the value inside you're js file so you can put it inside the fileAttachments variable like:

const fileAttachments = new Map([["flare-2.json",new URL(filePath ,import.meta.url)]]);

1 Comment

Somehow any modification to the .js file causes it to stop working. Just adding the const filePath variable stopped it working. I changed the privileges on the file but this didn't help either
0

The solution is;

  1. Create a session variable in php;
$file_path = "../files/$user";
$_SESSION['file_path'] = $file_path;


  1. Declare a script in the html head declaring a javascript global variable;
   <script> 
       var filePath ="<?php session_start(); echo $_SESSION['file_path'];?>"
   </script>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.