PHPackages                             hyvor/phrosemirror - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. hyvor/phrosemirror

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

hyvor/phrosemirror
==================

Prosemirror in PHP

1.0.5(1y ago)79.6k↓33.8%[3 issues](https://github.com/hyvor/phrosemirror/issues)MITPHPPHP &gt;8.1CI passing

Since Oct 31Pushed 1y ago1 watchersCompare

[ Source](https://github.com/hyvor/phrosemirror)[ Packagist](https://packagist.org/packages/hyvor/phrosemirror)[ RSS](/packages/hyvor-phrosemirror/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (10)Dependencies (3)Versions (22)Used By (0)

Phrosemirror is a PHP library to work with Prosemirror (or TipTap) JSON content in an easy and type-safe way.

Here is what this library can do:

- Convert Prosemirror JSON into a Document with typed Nodes, Marks, and Attributes
- Analyze and change Documents
- Convert a Document to HTML
- Convert a Document to Text
- Parse HTML to a Document
- `content` and `group` for more strict schema conformity

Installation
------------

[](#installation)

```
composer require hyvor/phrosemirror

```

1. Schema
---------

[](#1-schema)

This library is unopinionated, which means there is no default schema. To start, you have to start with defining your schema that is similar to your front-end Prosemirror configurations.

> You can find an example schema in the `/example` directory in this repo, which is similar to `prosemirror-schema-basic` package's schema.

```
use Hyvor\Phrosemirror\Types\Schema;

$schema = new Schema(
    [
        new Doc,
        new Text,
        new Paragraph,
        new Blockquote,
        new Image
    ],
    [
        new Strong,
        new Italic,
    ]
);
```

In the `Schema` constructor, first argument is an array of **Nodes Types** and the second one is an array of **Marks Types**.

### Node Types

[](#node-types)

A basic node type looks like this:

```
use Hyvor\Phrosemirror\Types\NodeType;

class Doc extends NodeType
{
    public string $name = 'doc';
    public ?string $content = 'block+';
}
```

They can contain `content` and `group` properties. If `content` is not set, no content is allowed in this node. See [Content &amp; Grouping](#content--grouping) below for more information on how these properties work.

Here is another example of a Node Type:

```
class Paragraph extends NodeType
{
    public string $name = 'paragraph';
    public ?string $content = 'inline*';
    public string $group = 'block';
}
```

### Mark Types

[](#mark-types)

A basic mark type looks like this:

```
use Hyvor\Phrosemirror\Types\MarkType;

class Strong extends MarkType
{
    public string $name = 'strong';
}
```

### Attributes (Attrs)

[](#attributes-attrs)

One main goal of this library is to achieve type-safety. Therefore, attributes are defined in a typed class.

```
use Hyvor\Phrosemirror\Types\AttrsType;

class ImageAttrs extends AttrsType
{

    public string $src;
    public ?string $alt;

}
```

> By defining explicit types, we are sure that `src` attribute of the Image is always a string. `alt` can be a string or null.

You can also define default values for attributes, which will be used if they are not present in the JSON document.

```
class ImageAttrs extends AttrsType
{
    public string $src = 'https://hyvor.com/placeholder.png';
}
```

Then, in the Node Type or Mark Type, you have to mention the Attrs class.

```
use Hyvor\Phrosemirror\Types\NodeType;

class Image extends NodeType
{
    // ...
    public string $attrs = ImageAttrs::class;
}
```

2. Document
-----------

[](#2-document)

Once the Schema is ready, we can start working with Documents.

```
use Hyvor\Phrosemirror\Types\Schema;
use Hyvor\Phrosemirror\Document\Document;

$schema = new Schema($nodes, $marks);
$json = '{}'; //  `TextNode` is a special `Node` that represents the `text` node type in Prosemirror. It has the `string $text` property in addition to the above properties. Also, `$marks` only makes sense in the context of `TextNode`.

#### Checking Node Type

[](#checking-node-type)

Use `isOfType()` to check if a `Node` is of a particular `NodeType` defined in your schema.

```
$json = ['type' => 'paragraph'];
$node = Node::fromJson($schema, $json);

$node->isOfType(Paragraph::class); // true
$node->isOfType(Image::class); // false
$node->isOfType([Paragraph::class, Image::class]); // true
```

#### Accessing Attributes

[](#accessing-attributes)

Use the `attr()` method to access an attribute of the Node.

```
$json = ['type' => 'image', 'attrs' => ['src' => 'image.png']];
$image = Node::fromJson($schema, $json);

// html-escaped (safe to use in HTML output)
$src = $image->attr('src');

// not html-escaped
$src = $image->attr('src', escape: false);
```

#### Traversing Through Nested Nodes

[](#traversing-through-nested-nodes)

You can traverse through nested nodes using the `traverse()` method with a callback. Here is an example that traverse through all nodes and finds all image nodes.

```
$document = Document::fromJson($schema, $json);

$images = [];
$document->traverse(function(Node $node) use(&$images) {
    if ($node->isOfType(Image::class)) {
        $images[] = $node;
    }
})
```

> `traverse()` traverses through `TextNode`s too!

#### Traversing Through Direct Children

[](#traversing-through-direct-children)

Use `foreach` with `$node->content`.

```
foreach ($node->content as $child) {
    if ($child->isOfType(Image::class)) {
        echo "I found an image!";
    }
}
```

#### Finding Nodes

[](#finding-nodes)

Earlier, we used `traverse()` to find nodes, but there is the `getNodes()` method to make it easier. It searches through the all nested nodes and returns `Node[]` of matched nodes.

```
// images
$node->getNodes(Image::class);

// all nodes (including TextNodes)
$node->getNodes();

// nodes of multiple types
$node->getNodes([Paragraph::class, Blockquote::class]);

// images (only direct children)
$node->getNodes(Image::class, false);
```

#### Finding Marks

[](#finding-marks)

Similar to `getNodes()` you can use `getMarks()` to find marks within the current node. It searches all nested nodes and returns `Mark[]` of matched marks.

```
// links
$node->getMarks(Link::class);

// all marks
$node->getMarks();

// multiple types
$node->getMarks([Strong::class, Italic::class]);

// without nesting (marks of the current node only)
$node->getMarks(Link::class, false);
```

#### JSON Serialize

[](#json-serialize)

You can serialize a Node/Document back to JSON.

```
$node->toJson(); // JSON string
$node->toArray(); // PHP array
```

### Mark

[](#mark)

```
namespace Hyvor\Phrosemirror\Document;

use Hyvor\Phrosemirror\Types\MarkType;
use Hyvor\Phrosemirror\Types\AttrsType;

class Mark
{
    public MarkType $type;
    public AttrsType $attrs;
}
```

`$type` and `$attrs` are analogous to those of Node's.

`Mark` has `isOfType()`, `attr()`, `toArray()`, and `toJson()`, which works similar to `Node`'s methods.

```
$mark = Mark::fromJson(['type' => 'link', 'attrs' => ['src' => 'https://hyvor.com']);

$mark->isOfType(Strong::class); // false
$mark->attr('src'); // https://hyvor.com
```

### Fragment

[](#fragment)

`$node->content` is a `Fragment`. It contains an array of children nodes. You can think of it just as an array, but with helper methods that makes things easier.

```
$fragment = $node->content();

// READ

$fragment->first(); // Node | null
$fragment->last(); // Node | null
$fragment->nth(2); // Node | null

$fragment->count(); // int

// get all Nodes in the Fragment as an array
$fragment->all(); // Node[]

// loop through each node
$fragment->each(fn (Node $node) => false);

// WRITE (Be careful, these methods changes the document)

$fragment->addNodeToStart($node);
$fragment->addNodeToEnd($node);
$fragment->addNode($node); // same as addNodeToEnd
$fragment->setNodes($nodes);
$fragment->map(fn (Node $node) => $node); // update nodes in a callback
```

3. HTML
-------

[](#3-html)

Next, let's convert your document to HTML. To do this, you have to define the `toHtml()` method in Node Types and Mark Types.

```
use Hyvor\Phrosemirror\Document\Node;
use Hyvor\Phrosemirror\Types\NodeType;

class Paragraph extends NodeType
{

    public function toHtml(Node $node, string $children) : string
    {
        return "$children";
    }

}
```

`toHtml()` should return the HTML string of the node, placing the `$children` string in it.

Here is another example using the attributes of that Node.

```
use Hyvor\Phrosemirror\Document\Node;
use Hyvor\Phrosemirror\Types\NodeType;

class Image extends NodeType
{

    public function toHtml(Node $node, string $children) : string
    {
        $src = $node->attr('src');
        return "$children";
    }

}
```

> Do not directly use `$node->attrs->src` as the raw attributes are not HTML-escaped. Always use `$node->attr()` or `$node->attrs->get()`

### HTML: Document -&gt; HTML

[](#html-document---html)

Use the `toHtml()` method to serialize a document (or any node) to HTML.

```
$document = Document::fromJson($schema, $json);
$html = $document->toHtml();
```

Parsing HTML
------------

[](#parsing-html)

The `HtmlParser` class is responsible for parsing HTML to a Document. It takes the Schema and some parsing rules to parse the HTML.

```
